#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Options
"""
import json
import os
import pkgutil
import shlex
from argparse import ArgumentParser

import six


def build_argument_parser():
    """
    Builds the argument parser
    :return: the argument parser
    :rtype: ArgumentParser
    """
    opts = ArgumentParser()
    opts.add_argument(dest='filename', help='Filename or release name to guess', nargs='*')

    naming_opts = opts.add_argument_group("Naming")
    naming_opts.add_argument('-t', '--type', dest='type', default=None,
                             help='The suggested file type: movie, episode. If undefined, type will be guessed.')
    naming_opts.add_argument('-n', '--name-only', dest='name_only', action='store_true', default=None,
                             help='Parse files as name only, considering "/" and "\\" like other separators.')
    naming_opts.add_argument('-Y', '--date-year-first', action='store_true', dest='date_year_first', default=None,
                             help='If short date is found, consider the first digits as the year.')
    naming_opts.add_argument('-D', '--date-day-first', action='store_true', dest='date_day_first', default=None,
                             help='If short date is found, consider the second digits as the day.')
    naming_opts.add_argument('-L', '--allowed-languages', action='append', dest='allowed_languages', default=None,
                             help='Allowed language (can be used multiple times)')
    naming_opts.add_argument('-C', '--allowed-countries', action='append', dest='allowed_countries', default=None,
                             help='Allowed country (can be used multiple times)')
    naming_opts.add_argument('-E', '--episode-prefer-number', action='store_true', dest='episode_prefer_number',
                             default=None,
                             help='Guess "serie.213.avi" as the episode 213. Without this option, '
                                  'it will be guessed as season 2, episode 13')
    naming_opts.add_argument('-T', '--expected-title', action='append', dest='expected_title', default=None,
                             help='Expected title to parse (can be used multiple times)')
    naming_opts.add_argument('-G', '--expected-group', action='append', dest='expected_group', default=None,
                             help='Expected release group (can be used multiple times)')

    input_opts = opts.add_argument_group("Input")
    input_opts.add_argument('-f', '--input-file', dest='input_file', default=None,
                            help='Read filenames from an input text file. File should use UTF-8 charset.')

    output_opts = opts.add_argument_group("Output")
    output_opts.add_argument('-v', '--verbose', action='store_true', dest='verbose', default=None,
                             help='Display debug output')
    output_opts.add_argument('-P', '--show-property', dest='show_property', default=None,
                             help='Display the value of a single property (title, series, video_codec, year, ...)')
    output_opts.add_argument('-a', '--advanced', dest='advanced', action='store_true', default=None,
                             help='Display advanced information for filename guesses, as json output')
    output_opts.add_argument('-j', '--json', dest='json', action='store_true', default=None,
                             help='Display information for filename guesses as json output')
    output_opts.add_argument('-y', '--yaml', dest='yaml', action='store_true', default=None,
                             help='Display information for filename guesses as yaml output')

    conf_opts = opts.add_argument_group("Configuration")
    conf_opts.add_argument('-c', '--config', dest='config', action='append', default=None,
                           help='Filepath to the configuration file. Configuration contains the same options as '
                                'those command line options, but option names have "-" characters replaced with "_". '
                                'If not defined, guessit tries to read a configuration default configuration file at '
                                '~/.guessit/options.(json|yml|yaml) and ~/.config/guessit/options.(json|yml|yaml). '
                                'Set to "false" to disable default configuration file loading.')
    conf_opts.add_argument('--no-embedded-config', dest='no_embedded_config', action='store_true',
                           default=None,
                           help='Disable default configuration.')

    information_opts = opts.add_argument_group("Information")
    information_opts.add_argument('-p', '--properties', dest='properties', action='store_true', default=None,
                                  help='Display properties that can be guessed.')
    information_opts.add_argument('-V', '--values', dest='values', action='store_true', default=None,
                                  help='Display property values that can be guessed.')
    information_opts.add_argument('--version', dest='version', action='store_true', default=None,
                                  help='Display the guessit version.')

    return opts


def parse_options(options=None, api=False):
    """
    Parse given option string

    :param options:
    :type options:
    :param api
    :type boolean
    :return:
    :rtype:
    """
    if isinstance(options, six.string_types):
        args = shlex.split(options)
        options = vars(argument_parser.parse_args(args))
    elif options is None:
        if api:
            options = {}
        else:
            options = vars(argument_parser.parse_args())
    elif not isinstance(options, dict):
        options = vars(argument_parser.parse_args(options))
    return options


argument_parser = build_argument_parser()


class ConfigurationException(Exception):
    """
    Exception related to configuration file.
    """
    pass


def load_config(options):
    """
    Load configuration from configuration file, if defined.
    :param options:
    :type options:
    :return:
    :rtype:
    """
    config_files_enabled = True
    custom_config_files = None
    if options.get('config') is not None:
        custom_config_files = options.get('config')
        if not custom_config_files \
                or not custom_config_files[0] \
                or custom_config_files[0].lower() in ['0', 'no', 'false', 'disabled']:
            config_files_enabled = False

    configurations = []
    if config_files_enabled:
        home_directory = os.path.expanduser("~")
        cwd = os.getcwd()
        yaml_supported = False
        try:
            import yaml  # pylint: disable=unused-variable
            yaml_supported = True
        except ImportError:
            pass
        config_file_locations = get_config_file_locations(home_directory, cwd, yaml_supported)
        config_files = [f for f in config_file_locations if os.path.exists(f)]

        if custom_config_files:
            config_files = config_files + custom_config_files

        for config_file in config_files:
            config_file_options = load_config_file(config_file)
            if config_file_options:
                configurations.append(config_file_options)

    if not options.get('no_embedded_config'):
        embedded_options_data = pkgutil.get_data('guessit', 'config/options.json').decode("utf-8")
        embedded_options = json.loads(embedded_options_data)
        configurations.append(embedded_options)

    if configurations:
        configurations.append(options)
        return merge_configurations(*configurations)
    else:
        return options


def merge_configurations(*configurations):
    """
    Merge configurations into a single options dict.
    :param configurations:
    :type configurations:
    :return:
    :rtype:
    """

    merged = {}

    for options in configurations:
        pristine = options.get('pristine')

        if pristine:
            if pristine is True:
                merged = {}
            else:
                for to_reset in pristine:
                    if to_reset in merged:
                        del merged[to_reset]

        for (option, value) in options.items():
            if value is not None and option != 'pristine':
                if option in merged.keys() and isinstance(merged[option], list):
                    merged[option].extend(value)
                elif isinstance(value, list):
                    merged[option] = list(value)
                else:
                    merged[option] = value

    return merged


def load_config_file(filepath):
    """
    Load a configuration as an options dict.

    Format of the file is given with filepath extension.
    :param filepath:
    :type filepath:
    :return:
    :rtype:
    """
    if filepath.endswith('.json'):
        with open(filepath) as config_file_data:
            return json.load(config_file_data)
    if filepath.endswith('.yaml') or filepath.endswith('.yml'):
        try:
            import yaml
            with open(filepath) as config_file_data:
                return yaml.load(config_file_data)
        except ImportError:  # pragma: no cover
            raise ConfigurationException('Configuration file extension is not supported. '
                                         'PyYAML should be installed to support "%s" file' % (
                                             filepath,))
    raise ConfigurationException('Configuration file extension is not supported for "%s" file.' % (filepath,))


def get_config_file_locations(homedir, cwd, yaml_supported=False):
    """
    Get all possible locations for configuration file.
    :param homedir: user home directory
    :type homedir: basestring
    :param cwd: current working directory
    :type homedir: basestring
    :return:
    :rtype: list
    """
    locations = []

    configdirs = [(os.path.join(homedir, '.guessit'), 'options'),
                  (os.path.join(homedir, '.config', 'guessit'), 'options'),
                  (cwd, 'guessit.options')]
    configexts = ['json']

    if yaml_supported:
        configexts.append('yaml')
        configexts.append('yml')

    for configdir in configdirs:
        for configext in configexts:
            locations.append(os.path.join(configdir[0], configdir[1] + '.' + configext))

    return locations
