#!/usr/bin/env python3

"""
Creates all the various image sizes and file structure for a web application.

`python generate.py -h`

"""

import os
import sys
import logging
import subprocess
import optparse

sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__))))

import config

log = logging.getLogger('V3D-IG')
log.setLevel(logging.INFO)
logH = logging.StreamHandler(sys.stdout)
logH.setFormatter(logging.Formatter('%(name)s-%(levelname)s: %(message)s'))
log.addHandler(logH)

class Converter():
    """
    Takes an icon image and splash image and generates a folder that includes
    all of the various filenames and resized images necessary for a
    multi-platform cordova build.

    Edit the platform configuration in config.py to adjust which files
    are created for each platform.

    TODO:
    - support more control over imagemagick settings
    - portrait vs landscape (include in naming schemes)
    - generate config.xml
    """

    def __init__(self, platform_config):
        self.platform_config = platform_config
        self.parse_command_line()
        self.verify_dependencies()
        log.debug('Initialized icon converter')

    def verify_dependencies(self):

        if os.name == 'nt':
            # disable popup console window
            si = subprocess.STARTUPINFO()
            si.dwFlags = subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = subprocess.SW_HIDE
        else:
            si = None

        # try to run convert command - if it fails, tell user and bail
        if subprocess.call(
                [self.settings.convert_path, '-version'],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
                startupinfo=si):
            log.error('Could not find ImageMagick `convert` method. Please install ' +
                          'ImageMagick and/or add it to the path')
            sys.exit(1)

    def parse_command_line(self):

        parser = optparse.OptionParser()
        parser.add_option('-v',
                dest='verbose',
                action='store_true',
                default=False,
                help='log all the things'
            )
        parser.add_option('--convert-path',
                dest='convert_path',
                action='store',
                default='convert',
                help='path to imagemagik `convert` executable'
            )
        parser.add_option('--cordova-icon',
                dest='cordova_icon',
                action='store',
                help='relative path to Cordova icon source image'
            )
        parser.add_option('--favicon',
                dest='favicon',
                action='store',
                help='relative path to favicon source image'
            )
        parser.add_option('--cordova-splash',
                action='store',
                dest='cordova_splash',
                help='relative path to Cordova splash source image'
            )
        parser.add_option('--destination',
                action='store',
                dest='destination',
                default='.',
                help='relative path where you want the output created'
            )

        self.settings, _ = parser.parse_args(sys.argv)

        # set log verbosity from command line arguments
        if self.settings.verbose:
            log.setLevel(logging.DEBUG)

        if (self.settings.cordova_icon is None and
                self.settings.favicon is None and
                self.settings.cordova_splash is None):
            parser.print_help()
            sys.exit(1)

    def generate(self):
        for platform, settings in self.platform_config.items():
            self.generate_platform(platform, settings)

    def generate_platform(self, platform_name, settings):
        log.info('Processing images for {} platform'.format(platform_name))

        if 'cordova_icon' in settings and self.settings.cordova_icon is not None:
            if os.path.exists(self.settings.cordova_icon):
                destination_dir = self.get_cordova_icon_path(platform_name)
                log.debug('Creating Cordova icon path {} if necessary'.format(destination_dir))
                try:
                    os.makedirs(destination_dir)
                except OSError:
                    pass

                self.generate_platform_icons(self.settings.cordova_icon, destination_dir, settings['cordova_icon'])
            else:
                log.error('Icon path not found: ' + self.settings.cordova_icon)

        if 'favicon' in settings and self.settings.favicon is not None:
            if os.path.exists(self.settings.favicon):
                destination_dir = self.get_favicon_path()
                log.debug('Creating favicon path {} if necessary'.format(destination_dir))
                try:
                    os.makedirs(destination_dir)
                except OSError:
                    pass

                self.generate_platform_icons(self.settings.favicon, destination_dir, settings['favicon'])
            else:
                log.error('Icon path not found: ' + self.settings.favicon)

        if 'cordova_splash' in settings and self.settings.cordova_splash is not None:
            if os.path.exists(self.settings.cordova_splash):
                destination_dir = self.get_splash_path(platform_name)
                log.debug('Creating splash path {} if necessary'.format(destination_dir))
                try:
                    os.makedirs(destination_dir)
                except OSError:
                    pass

                self.generate_platform_splashes(destination_dir, settings['cordova_splash'])
            else:
                log.error('Splash path not found: ' + self.settings.cordova_splash)

    def get_cordova_icon_path(self, platform_name):
        return os.path.abspath(os.path.join(self.settings.destination, 'res', 'icon', platform_name))

    def get_favicon_path(self):
        return os.path.abspath(os.path.join(self.settings.destination, 'media'))

    def get_splash_path(self, platform_name):
        return os.path.join(self.settings.destination, 'res', 'screen', platform_name)

    def generate_platform_icons(self, source, destination_dir, icon_settings):
        log.debug('Creating icons')

        for icon_type in icon_settings:
            for size_config in icon_type['sizes']:
                # parse size config into standard
                size, dpi_level = self._parse_icon_size_config(size_config)
                halfsize = int(size / 2)
                thirdsize = int(size / 3)

                # create destination string from filename pattern
                filename = icon_type['filename'].format(
                    size=size,
                    halfsize=halfsize,
                    thirdsize=thirdsize,
                    dpi_level=dpi_level
                )

                destination = os.path.join(destination_dir, filename)

                # if background is specified send it
                background = None
                if 'background' in icon_type:
                    background = icon_type['background']

                # resize icon and put it where it belongs
                self.resize(
                    source, destination, size, size, background
                )

    def _parse_icon_size_config(self, size_config):
        dpi_level = 'default'
        if type(size_config) == type(0):
            size = size_config
        elif len(size_config) > 1:
            size, dpi_level = size_config
        return size, dpi_level

    def generate_platform_splashes(self, destination_dir, splash_settings):
        log.debug('Creating splash screens')

        for splash_type in splash_settings:
            for size_config in splash_type['sizes']:
                # parse size config into standard
                width, height, dpi_level = self._parse_splash_size_config(size_config)
                halfwidth = int(width / 2)
                halfheight = int(height / 2)

                # create destination string from filename pattern
                filename = splash_type['filename'].format(
                    width=width,
                    height=height,
                    halfwidth=halfwidth,
                    halfheight=halfheight,
                    dpi_level=dpi_level
                )

                destination = os.path.join(destination_dir, filename)

                # if background is specified send it
                background = None
                if 'background' in splash_type:
                    background = splash_type['background']

                # resize spalsh and put it where it belongs
                self.resize(
                    self.settings.cordova_splash, destination, width, height, background
                )

    def _parse_splash_size_config(self, size_config):
        dpi_level = 'default'
        if type(size_config) == type(0):
            width = height = size_config
        else:
            if len(size_config) == 1:
                width = height = size_config[0]
            elif len(size_config) == 2:
                width, height = size_config
            elif len(size_config) == 3:
                width, height, dpi_level = size_config

        return width, height, dpi_level

    def resize(self, source, destination, width, height, background=None):
        log.debug('Creating {} ({}, {})'.format(destination, str(width), str(height)))

        self._resize_imagemagick(source, destination, width, height, background)

    def _resize_imagemagick(self, source, destination, width, height, background=None):
        command = [self.settings.convert_path]

        if background:
            command.append('-background')
            command.append(background)
            command.append('-flatten')
        else:
            command.append('-background')
            command.append('none')

        command.append(source)

        command.append('-resize')
        command.append('{bigger}x{bigger}'.format(bigger=max(width, height)))

        command.append('-gravity')
        command.append('center')

        command.append('-extent')
        command.append('{}x{}'.format(width, height))
        command.append(destination)

        log.debug(command)

        if os.name == 'nt':
            # disable popup console window
            si = subprocess.STARTUPINFO()
            si.dwFlags = subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = subprocess.SW_HIDE
        else:
            si = None

        subprocess.call(command, startupinfo=si)

if __name__ == '__main__':
    converter = Converter(config.PLATFORMS)
    converter.generate()
