# -*- coding: utf-8 -*-
import sys
import curses
import logging
from argparse import ArgumentParser, SUPPRESS as SUPPRESS
from os import path, getenv, environ
from sys import platform, version_info
from contextlib import contextmanager
from platform import system

from .radio import PyRadio
from .config import PyRadioConfig, SUPPORTED_PLAYERS
from .install import PyRadioUpdate, PyRadioUpdateOnWindows, is_pyradio_user_installed, version_string_to_list, get_github_tag

PATTERN = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

PY3 = sys.version[0] == '3'

@contextmanager
def pyradio_config_file():
    cf = PyRadioConfig()
    try:
        yield cf
    finally:
        try:
            ret, lfile = cf.remove_session_lock_file()
            if cf.force_to_remove_lock_file:
                if ret == 0:
                    print('Lock file removed: "{}"'.format(lfile))
                elif ret == 1:
                    print('Failed to remove Lock file: "{}"'.format(lfile))
                else:
                 print('Lock file not found: "{}"'.format(lfile))
        except:
            pass

def __configureLogger():
    logger = logging.getLogger('pyradio')
    logger.setLevel(logging.DEBUG)

    # Handler
    fh = logging.FileHandler(path.join(path.expanduser('~'), 'pyradio.log'))
    fh.setLevel(logging.DEBUG)

    # create formatter
    formatter = logging.Formatter(PATTERN)

    # add formatter to ch
    fh.setFormatter(formatter)

    # add ch to logger
    logger.addHandler(fh)

def shell():
    version_too_old = False
    if sys.version_info[0] == 2:
        if sys.version_info < (2, 7):
            version_too_old = True
        elif sys.version_info.major == 3 and sys.version_info < (3, 5):
            version_too_old = True
    if version_too_old:
        print('PyRadio requires python 2.7 or 3.5+...')
        sys.exit(1)

    requested_player = ''
    parser = ArgumentParser(description='Curses based Internet radio player')
    parser.add_argument('-s', '--stations', default='',
                        help='Use specified station CSV file.')
    parser.add_argument('-p', '--play', nargs='?', default='False',
                        help='Start and play.'
                        'The value is num station or empty for random.')
    parser.add_argument('-u', '--use-player', default='',
            help='Use specified player. '
            'A comma-separated list can be used to specify detection order. '
            'Supported players: mpv, mplayer, vlc.')
    parser.add_argument('-a', '--add', action='store_true',
                        help='Add station to list.')
    parser.add_argument('-ls', '--list-playlists', action='store_true',
                        help='List of available playlists in config dir.')
    parser.add_argument('-l', '--list', action='store_true',
                        help='List of available stations in a playlist.')
    parser.add_argument('-t', '--theme', default='', help='Use specified theme.')
    parser.add_argument('-tlp', '--toggle-load-last-playlist', action='store_true',
                        help='Toggle autoload last opened playlist.')
    parser.add_argument('-scd', '--show-config-dir', action='store_true',
                        help='Print config directory [CONFIG DIR] location and exit.')
    parser.add_argument('-ocd', '--open-config-dir', action='store_true',
                        help='Open config directory [CONFIG DIR] with default file manager.')
    parser.add_argument('-ep', '--extra-player_parameters', default=None,
                        help="Provide extra player parameters as a string. The parameter is saved in the configuration file and is activated for the current session. The string\'s format is [player_name:parameters]. player_name can be 'mpv', 'mplayer' or 'vlc'. Alternative format to pass a profile: [player_name:profile:profile_name]. In this case, the profile_name must be a valid profile defined in the player\'s config file (not for VLC).")
    parser.add_argument('-ap', '--active-player-param-id', default=0, help='Specify the extra player parameter set to be used with the default player. ACTIVE_PLAYER_PARAM_ID is 1-11 (refer to the output of the -lp option)')
    parser.add_argument('-lp', '--list-player-parameters', default=None,
                        action='store_true',
                        help='List extra players parameters.')
    parser.add_argument('-U', '--update', action='store_true',
                        help='Update PyRadio.')
    if platform.startswith('linux'):
        parser.add_argument('--user', action='store_true', default=False,
                            help='Install only for current user (linux only).')
    parser.add_argument('-R', '--uninstall', action='store_true',
                        help='Uninstall PyRadio.')
    parser.add_argument('--unlock', action='store_true',
                        help="Remove sessions' lock file.")
    parser.add_argument('-d', '--debug', action='store_true',
                        help='Start pyradio in debug mode.')
    parser.add_argument('-V', '--version', action='store_true',
                        help='Display version information.')
    ''' extra downloads
        only use them after the developer says so,
        for debug purposes only
            --devel           download official devel branch
            --sng-master      download developer release (master)
            --sng-devel       download developer devel branch
            --force-update    give a versio > than current,
                              to check update notification functionality
    '''
    parser.add_argument('--sng-master', action='store_true', help=SUPPRESS)
    parser.add_argument('--sng-devel', action='store_true', help=SUPPRESS)
    parser.add_argument('--devel', action='store_true', help=SUPPRESS)
    parser.add_argument('--force-update', default='', help=SUPPRESS)
    args = parser.parse_args()
    sys.stdout.flush()

    config_already_read = False

    with pyradio_config_file() as pyradio_config:

        if args.version:
            pyradio_config.get_pyradio_version()
            print('PyRadio version: {}'.format(pyradio_config.current_pyradio_version))
            print('Python version: {}'.format(sys.version.replace('\n', ' ').replace('\r', ' ')))
            pyradio_config.read_config()
            if pyradio_config.distro != 'None':
                print('Distribution: {}'.format(pyradio_config.distro))
            sys.exit()

        if args.toggle_load_last_playlist:
            if pyradio_config.locked:
                print('Error: Another instance of PyRadio is already running!')
                print('       Please close it and try again...')
                sys.exit(1)
            else:
                read_config(pyradio_config)
                pyradio_config.opts['open_last_playlist'][1] = not pyradio_config.opts['open_last_playlist'][1]
                pyradio_config.opts['dirty_config'][1] =  True
                print('Setting auto load last playlist to: {}'.format(pyradio_config.opts['open_last_playlist'][1]))
                save_config()
            sys.exit(0)

        package = 0
        if args.uninstall or args.update:
            if args.sng_master:
                package = 1
            elif args.sng_devel:
                package = 2
            elif args.devel:
                package = 3
            if not config_already_read:
                read_config(pyradio_config)
                config_already_read = True
            if pyradio_config.distro != 'None' and \
                    not platform.startswith('win'):
                no_update(args.uninstall)

        if args.update:
            if package == 0:
                pyradio_config.get_pyradio_version()
                last_tag = get_github_tag()
                if last_tag:
                    print('Released version   :  {}'.format(last_tag))
                    print('Installed version  :  {}'.format(pyradio_config.current_pyradio_version))
                    if version_string_to_list(last_tag) <= version_string_to_list(pyradio_config.current_pyradio_version):
                        print('Latest version already installed. Nothing to do....')
                        sys.exit()
                else:
                    print('Error reading online version.\nPlease make sure you are connected to the internet and try again.')
                    sys.exit(1)

            python_version_to_use = 3 if PY3 else 2
            try:
                upd = PyRadioUpdate(
                    package=package,
                    python_version_to_use=python_version_to_use
                )
                if platform.startswith('linux'):
                    upd.user = args.user
                upd.update_pyradio()
            except RuntimeError:
                upd = PyRadioUpdateOnWindows(
                    package=package,
                    python_version_to_use=python_version_to_use
                )
                upd.update_or_uninstall_on_windows(mode='update-open')
            sys.exit()

        if args.uninstall:
            python_version_to_use = 3 if PY3 else 2
            try:
                upd = PyRadioUpdate(
                    package=package,
                    python_version_to_use=python_version_to_use
                )
                upd.remove_pyradio()
            except RuntimeError:
                upd = PyRadioUpdateOnWindows(
                    package=package,
                    python_version_to_use=python_version_to_use
                )
                upd.update_or_uninstall_on_windows(mode='uninstall-open')
            sys.exit()

        ''' check conflicting parameters '''
        if args.active_player_param_id and \
                args.extra_player_parameters:
          print('Error: You cannot use parameters "-ep" and "-ap" together!\n')
          sys.exit(1)

        ''' user specified extra player parameter '''
        if args.active_player_param_id:
            try:
                a_param = int(args.active_player_param_id)
            except ValueError:
                print('Error: Parameter -ap is not a number\n')
                sys.exit(1)
            if 1 <= a_param <= 11:
                pyradio_config.user_param_id = a_param
            else:
                print('Error: Parameter -ap must be between 1 and 11')
                print('       Actually, it must be between 1 and the maximum')
                print('       number of parameters for your default player.\n')
                args.list_player_parameters = True

        ''' list extra player parameters '''
        if args.list_player_parameters:
            print('PyRadio Players Extra Parameters')
            print(32 * '-')
            read_config(pyradio_config)
            default_player_name = pyradio_config.opts['player'][1].replace(' ', '').split(',')[0]
            if default_player_name == '':
                default_player_name = SUPPORTED_PLAYERS[0]
            for a_player in SUPPORTED_PLAYERS:
                if default_player_name == a_player:
                    print('Player: ' + a_player + ' (default)')
                else:
                    print('Player: ' + a_player)
                default = 0
                for i, a_param in enumerate(pyradio_config.saved_params[a_player]):
                    if i == 0:
                        default = int(a_param)
                    else:
                        str_default = '(default)' if i == default else ''
                        count = str(i) if i > 9 else ' ' + str(i)
                        print('    {0}. {1} {2}'.format(count, a_param, str_default))
                print('')
            sys.exit()

        ''' extra player parameters '''
        if args.extra_player_parameters:
            if ':' in args.extra_player_parameters:
                if pyradio_config.locked:
                    print('Error: This session is locked!')
                    print('       Please exist any other instances of the program')
                    print('       that are currently running and try again.')
                    sys.exit(1)
                else:
                    if args.extra_player_parameters.startswith('vlc:profile'):
                        print('Error in parameter: "-ep".')
                        print('  VLC does not supports profiles\n')
                        sys.exit()
                    else:
                        pyradio_config.command_line_params = args.extra_player_parameters
            else:
                print('Error in parameter: "-ep".')
                print('  Parameter format: "player_name:parameters"')
                print('                 or "player_name:profile:name_of_profile"\n')
                sys.exit()

        if args.unlock:
            pyradio_config.locked = False
            pyradio_config.force_to_remove_lock_file = True
            sys.exit()

        if args.show_config_dir:
            print('PyRadio config dir: "{}"'.format(pyradio_config.stations_dir))
            sys.exit()

        if args.open_config_dir:
            open_conf_dir(pyradio_config)
            sys.exit()

        if args.list_playlists:
            pyradio_config.list_playlists()
            sys.exit()

        if args.list is False and args.add is False:
            print('Reading config...')
        if not config_already_read:
            read_config(pyradio_config)
            config_already_read = True

        if args.use_player != '':
            requested_player = args.use_player

        if args.list is False and args.add is False:
            print('Reading playlist...')
        sys.stdout.flush()
        is_last_playlist = False
        if pyradio_config.open_last_playlist:
            last_playlist = pyradio_config.get_last_playlist()
            if last_playlist:
                args.stations = last_playlist
                is_last_playlist = True
        ret = pyradio_config.read_playlist_file(
            stationFile=args.stations,
            is_last_playlist=is_last_playlist)
        if ret < 0:
            print_playlist_selection_error(args.stations, pyradio_config, ret)

        # No need to parse the file if we add station
        # Actually we do need to do so now, so that we
        # handle 2-column vs. 3-column playlists
        if args.add:
            if sys.version_info < (3, 0):
                params = raw_input("Enter the name: "), raw_input("Enter the url: "), raw_input("Enter the encoding (leave empty for '" + pyradio_config.default_encoding + "'): ")
            else:
                params = input("Enter the name: "), input("Enter the url: "), input("Enter the encoding (leave empty for '" + pyradio_config.default_encoding + "'): ")
            msg = ('name', 'url')
            for i, a_param in enumerate(params):
                if i < 2:
                    if a_param.strip() == '':
                        print('** Error: No {} entered. Aborting...'.format(msg[i]))
                        sys.exit(1)
            ret = pyradio_config.append_station(params, args.stations)
            if ret < 0:
                print_playlist_selection_error(args.stations, pyradio_config, ret)
            sys.exit()

        if args.list:
            header_format_string, format_string = get_format_string(pyradio_config.stations)
            header_string = header_format_string.format('[Name]','[URL]','[Encoding]')
            print(header_string)
            print(len(header_string) * '-')
            for num, a_station in enumerate(pyradio_config.stations):
                if a_station[2]:
                    encoding = a_station[2]
                else:
                    encoding = pyradio_config.default_encoding
                print(format_string.format(str(num+1), a_station[0], a_station[1], encoding))
            sys.exit()

        if args.debug:
            __configureLogger()
            if platform.startswith('win'):
                print('Debug mode activated\n  printing messages to file: "{}\pyradio.log"'.format(getenv('USERPROFILE')))
            else:
                print('Debug mode activated; printing messages to file: "~/pyradio.log"')
        else:
            ''' Refer to https://docs.python.org/3.7/howto/logging.html
                section "What happens if no configuration is provided"
            '''
            logging.raiseExceptions = False
            logging.lastResort = None

        if requested_player == '':
            requested_player = pyradio_config.player
        #else:
        #    pyradio_config.requested_player = requested_player

        if args.play == 'False':
            if args.stations == '':
                args.play = pyradio_config.default_station
        elif args.play is not None:
            try:
                check_int = int(args.play)
            except:
                print('Error: Invalid parameter (-p ' + args.play + ')')
                sys.exit(1)
        if args.play == '-1':
            args.play = 'False'

        ''' get auto play last playlist data '''
        if pyradio_config.last_playlist_to_open != []:
            pre_select = pyradio_config.last_playlist_to_open[1]
            if pyradio_config.last_playlist_to_open[2] > -1:
                args.play = str(pyradio_config.last_playlist_to_open[2] + 1)
            else:
                args.play = 'False'
        else:
            pre_select = 'False'

        theme_to_use = args.theme
        if not theme_to_use:
            theme_to_use = pyradio_config.theme

        # Starts the radio TUI.
        pyradio = PyRadio(
            pyradio_config,
            play=args.play,
            pre_select=pre_select,
            req_player=requested_player,
            theme=theme_to_use,
            force_update=args.force_update
        )
        ''' Setting ESCAPE key delay to 25ms
            Refer to: https://stackoverflow.com/questions/27372068/why-does-the-escape-key-have-a-delay-in-python-curses
        '''
        environ.setdefault('ESCDELAY', '25')

        ''' set window title '''
        if platform.startswith('win'):
            import ctypes
            try:
                if pyradio_config.locked:
                    win_title = 'PyRadio: Your Internet Radio Player (Session Locked)'
                else:
                    win_title = 'PyRadio: Your Internet Radio Player'
                ctypes.windll.kernel32.SetConsoleTitleW(win_title)
            except:
                pass
        else:
            try:
                if pyradio_config.locked:
                    sys.stdout.write('\x1b]2;PyRadio: Your Internet Radio Player (Session Locked)\x07')
                else:
                    sys.stdout.write('\x1b]2;PyRadio: Your Internet Radio Player\x07')
            except:
                pass
        sys.stdout.flush()

        ''' curses wrapper '''
        curses.wrapper(pyradio.setup)

        ''' curses is off '''
        if pyradio.setup_return_status:
            if pyradio_config.PROGRAM_UPDATE:
                if platform.startswith('win'):
                    upd = PyRadioUpdateOnWindows()
                    upd.update_or_uninstall_on_windows(mode='update-open')
                else:
                    upd = PyRadioUpdate()
                    upd.user = is_pyradio_user_installed()
                    upd.update_pyradio()
            else:
                print('\nThank you for using PyRadio. Cheers!')
        else:
            print('\nThis terminal can not display colors.\nPyRadio cannot function in such a terminal.\n')

def read_config(pyradio_config):
    ret = pyradio_config.read_config()
    if ret == -1:
        print('Error opening config: "{}"'.format(pyradio_config.config_file))
        sys.exit(1)
    elif ret == -2:
        print('Config file is malformed: "{}"'.format(pyradio_config.config_file))
        sys.exit(1)

def save_config(pyradio_config):
    ret = pyradio_config.save_config(from_command_line=True)
    if ret == -1:
        print('Error saving config!')
        sys.exit(1)

def no_update(uninstall):
    action = 'uninstall' if uninstall else 'update'
    print('PyRadio has been installed using either pip or your distribution\'s\npackage manager. Please use that to {} it.\n'.format(action))
    sys.exit(1)

def print_playlist_selection_error(a_selection, cnf, ret, exit_if_malformed=True):
    if exit_if_malformed:
        if ret == -1:
            print('Error: playlist is malformed: "{}"'.format(a_selection))
            sys.exit(1)

    if ret == -2:
        print('Error: Specified playlist not found')
        sys.exit(1)
    elif ret == -3:
        print('Error: Negative playlist number specified')
        sys.exit(1)
    elif ret == -4:
        print('Error: Specified numbered playlist not found')
        cnf.list_playlists()
        sys.exit(1)
    elif ret == -5:
        print('Error: Failed to write playlist')
        sys.exit(1)
    elif ret == -6:
        print('Error: Failed to rename playlist')
        sys.exit(1)
    elif ret == -7:
        print('Error: Playlist recovery failed!\n')
        if cnf.playlist_recovery_result == 1:
            msg = '''Both a playlist file (CSV) and a playlist backup file (TXT)
            exist for the selected playlist. In this case, PyRadio would
            try to delete the CSV file, and then rename the TXT file to CSV.\n
            Unfortunately, deleting the CSV file has failed, so you have to
            manually address the issue.'''
        else:
            msg = '''A playlist backup file (TXT) has been found for the selected
            playlist. In this case, PyRadio would try to rename this file
            to CSV.\n
            Unfortunately, renaming this file has failed, so you have to
            manually address the issue.'''
        print(msg)
        #open_conf_dir(cnf)
        sys.exit(1)
    elif ret == -8:
        print('File type not supported')
        sys.exit(1)

def open_conf_dir(cnf):
    import subprocess
    import os
    import platform
    if platform.system().lower() == 'windows':
        os.startfile(cnf.stations_dir)
    elif platform.system().lower() == 'darwin':
        subprocess.Popen(['open', cnf.stations_dir])
    else:
        subprocess.Popen(['xdg-open', cnf.stations_dir])

def get_format_string(stations):
    len0 = len1 = 0
    for n in stations:
        if len(n[0]) > len0:
            len0 = len(n[0])
        if len(n[1]) > len1:
            len1 = len(n[1])
    num = len(str(len(stations)))
    format_string = '{0:>' + str(num) + '.' + str(num) + 's}. ' + '{1:' + str(len0) + '.' + str(len0) + 's} | {2:' + str(len1) + '.' + str(len1) + 's} | {3}'
    header_format_string = '{0:' + str(len0+num+2) + '.' + str(len0+num+2) + 's} | {1:' + str(len1) + '.' + str(len1) + 's} | {2}'
    return header_format_string, format_string

if __name__ == '__main__':
    shell()
