import os, sys, traceback

import yaml

import defaults

CONFIG_FILENAME = 'vlcscheduler.yaml'
CONFIG_ENV_VAR = 'VLCSCHEDULER_YAML'
LOGGER_NAME = 'vlcscheduler'

config = None
logger = None


class ConfigLoadError(RuntimeError):
    pass


def locate_yaml_config():
    possible_locations = []
    
    if getattr(sys, 'frozen', False):
        exe_dir = os.path.dirname(sys.executable)
        
        if os.path.basename(exe_dir) == 'MacOS' and sys.platform == 'darwin':
            possible_locations.append(os.path.join(exe_dir, '..', '..', '..'))
        else:
            possible_locations.append(exe_dir)
    else:
        src_dir = os.path.dirname(os.path.abspath(__file__))
        
        if os.path.basename(src_dir) == 'src':
            possible_locations.append(os.path.dirname(src_dir))
        else:
            possible_locations.append(src_dir)
        
        possible_locations.append(os.getcwd())
    
    possible_locations = list(set(possible_locations))
    
    for location in possible_locations:
        path = os.path.join(location, CONFIG_FILENAME)
        
        if os.path.isfile(path):
            return path
    
    raise FileNotFoundError('Cannot find the configuration file %s in any of these places: %s.' % (
        CONFIG_FILENAME, ', '.join(possible_locations))
    )


def load_yaml_config():
    path = os.getenv(CONFIG_ENV_VAR) or locate_yaml_config()
    
    with open(path, 'r') as stream:
        try:
            config = yaml.safe_load(stream)
        except yaml.YAMLError as e:
            raise ConfigLoadError('%s does not contain valid YAML.' % CONFIG_FILENAME) from e
    
    return config


def build_config():
    config = type('Config', (object,), {})()
    yaml_config = load_yaml_config()
    
    for k in [k for k in dir(defaults) if k[0:1].isupper()]:
        new_v = getattr(defaults, k)
                
        if k.lower() in yaml_config:
            usr_v = yaml_config[k.lower()]
            
            if type(new_v) is dict and type(usr_v) is dict:
                new_v = {**new_v, **usr_v}
            else:
                new_v = usr_v
        
        setattr(config, k.upper(), new_v)
    
    return config


def initialize(*args, **kwargs):
    global config, logger
    
    if config is None:
        config = build_config()
    
    if logger is None:
        try:
            import coloredlogs
        except ImportError:
            coloredlogs = None
        finally:
            import logging
        
        logger = logging.getLogger(LOGGER_NAME)
        
        params = {
            'format': '%(asctime)s %(message)s',
            'datefmt': '[%H:%M:%S]'
        }
        
        if config.DEBUG:
            params['level'] = logging.DEBUG
        else:
            params['level'] = logging.INFO
        
        if coloredlogs:
            params['fmt'] = params.pop('format')
            coloredlogs.install(**params)
        else:
            logging.basicConfig(**params)
        
        # Other loggers
        if not config.DEBUG:
            logging.getLogger('schedule').setLevel(logging.WARNING)


def check_config():
    global config
    
    if len(config.SOURCES) == 0:
        raise RuntimeError('Please define at least one source in the configuration file.')
    
    for source in config.SOURCES:
        if not os.path.isdir(source['path']):
            raise RuntimeError('The source path is not a directory: %s.' % source['path'])
        
        if source.get('special') and source.get('play_every_minutes'):
            raise RuntimeError(
                'Simultaneous use of <special> and <play_every_minutes> for a '
                'single source is currently not supported.'
            )
    
    if not os.path.isfile(config.VLC.get('path', "")):
        raise RuntimeError('Invalid path to VLC: %s.' % config.VLC.get('path', None))


try:
    initialize()
    check_config()
except Exception as e:
    if not logger:
        import logging as logger
    
    if config and not config.DEBUG:
        logger.fatal(str(e))
    else:
        logger.fatal(traceback.format_exc())
    
    sys.exit(1)