# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-

import os
from platform import system
import json

from utils import util
from utils.util import log, join_path
from common.event import Event
import utils.ui as util_ui
import utils.util as util_utils
import user_data
from toxygen_wrapper.tests import support_testing as ts

global LOG
import logging
LOG = logging.getLogger('settings')

def merge_args_into_settings(args, settings):
    if args:
        if not hasattr(args, 'audio'):
            LOG.warn('No audio ' +repr(args))
        settings['audio'] = getattr(args, 'audio')
        if not hasattr(args, 'video'):
            LOG.warn('No video ' +repr(args))
        settings['video'] = getattr(args, 'video')
        for key in settings.keys():
            # proxy_type proxy_port proxy_host
            not_key = 'not_' +key
            if hasattr(args, key):
                val = getattr(args, key)
                if type(val) == bytes:
                    # proxy_host - ascii?
                    # filenames - ascii?
                    val = str(val, 'UTF-8')
                settings[key] = val
            elif hasattr(args, not_key):
                val = not getattr(args, not_key)
                settings[key] = val
    clean_settings(settings)
    return

def clean_settings(self):
    # failsafe to ensure C tox is bytes and Py settings is str

    # overrides
    self['mirror_mode'] = False
    # REQUIRED!!
    if not os.path.exists('/proc/sys/net/ipv6'):
        LOG.warn('Disabling IPV6 because /proc/sys/net/ipv6 does not exist')
        self['ipv6_enabled'] = False

    if 'proxy_type' in self and self['proxy_type'] == 0:
        self['proxy_host'] = ''
        self['proxy_port'] = 0

    if 'proxy_type' in self and self['proxy_type'] != 0 and \
        'proxy_host' in self and self['proxy_host'] != '' and \
        'proxy_port' in self and self['proxy_port'] != 0:
        if 'udp_enabled' in self and self['udp_enabled']:
            # We don't currently support UDP over proxy.
            LOG.info("UDP enabled and proxy set: disabling UDP")
        self['udp_enabled'] = False
        if 'local_discovery_enabled' in self and self['local_discovery_enabled']:
            LOG.info("local_discovery_enabled enabled and proxy set: disabling local_discovery_enabled")
        self['local_discovery_enabled'] = False
        if 'dht_announcements_enabled' in self and self['dht_announcements_enabled']:
            LOG.info("dht_announcements_enabled enabled and proxy set: disabling dht_announcements_enabled")
        self['dht_announcements_enabled'] = False

    if 'auto_accept_path' in self and \
       type(self['auto_accept_path']) == bytes:
        self['auto_accept_path'] = str(self['auto_accept_path'], 'UTF-8')

    for key in Settings.get_default_settings():
        if key not in self: continue
        if type(self[key]) == bytes:
            LOG.warn('bytes setting in: ' +key \
                     +' ' + repr(self[key]))
            # ascii?
            # self[key] = str(self[key], 'utf-8')
    LOG.debug("Cleaned settings")

def get_user_config_path():
    system = util_utils.get_platform()
    if system == 'Windows':
        return os.path.join(os.getenv('APPDATA'), 'Tox/')
    elif system == 'Darwin':
        return os.path.join(os.getenv('HOME'), 'Library/Application Support/Tox/')
    else:
        return os.path.join(os.getenv('HOME'), '.config/tox/')

def supported_languages():
    return {
        'English': 'en_EN',
        'French': 'fr_FR',
        'Russian': 'ru_RU',
        'Ukrainian': 'uk_UA'
    }

def built_in_themes():
    return {
        'dark': 'dark_style.qss',
        'default': 'style.qss'
    }

#def get_global_settings_path():
#    return os.path.join(get_base_directory(), 'toxygen.json')

def is_active_profile(profile_path):
    sFile = profile_path + '.lock'
    if not os.path.isfile(sFile):
        return False
    try:
        import psutil
    except Exception as e:
        return True
    with open(sFile, 'rb') as iFd:
        sPid = iFd.read()
    if sPid and int(sPid.strip()) in psutil.pids():
        return True
    LOG.debug('Unlinking stale lock file ' +sFile)
    try:
        os.unlink(sFile)
    except:
        pass
    return False

class Settings(dict):
    """
    Settings of current profile + global app settings
    """

    def __init__(self, toxes, json_path, app):
        self._toxes = toxes
        self._app = app
        self._path = app._path
        self._args = app._args
        self._oArgs = app._args
        self._log = lambda l: LOG.log(self._oArgs.loglevel, l)
        self._profile_path = json_path.replace('.json', '.tox')

        self._settings_saved_event = Event()
        path = json_path.replace('.tox', '.json')
        if path and os.path.isfile(path):
            try:
                with open(path, 'rb') as fl:
                    data = fl.read()
                if toxes.is_data_encrypted(data):
                    data = toxes.pass_decrypt(data)
                info = json.loads(str(data, 'utf-8'))
                LOG.debug('Parsed settings from: ' + str(path))
            except Exception as ex:
                title = f"Error opening/parsing settings file:"
                text = title +f"\n{path}\n"
                LOG.error(text +str(ex))
                util_ui.message_box(text, title)
                info = Settings.get_default_settings(app._args)
            user_data.settings.clean_settings(info)
        else:
            LOG.debug('get_default_settings for: ' + repr(path))
            info = Settings.get_default_settings(app._args)

        if not path or not os.path.exists(path):
            merge_args_into_settings(app._args, info)
        else:
            aC = self._changed(app._args, info)
            if aC:
                title = 'Override profile with commandline - '
                if path:
                    title += os.path.basename(path)
                text = 'Override profile with command-line settings? \n'
    #            text += '\n'.join([str(key) +'=' +str(val) for
    #                               key,val in self._changed(app._args).items()])
                text += repr(aC)
                reply = util_ui.question(text, title)
                if reply:
                    merge_args_into_settings(app._args, info)
        info['audio'] = getattr(app._args, 'audio')
        info['video'] = getattr(app._args, 'video')
        if getattr(app._args, 'trace_enabled'):
            info['trace_enabled'] = getattr(app._args, 'trace_enabled')
        else:
            LOG.warn("app._args, 'trace_enabled")
            info['trace_enabled'] = False
        super().__init__(info)
        self._upgrade()

        LOG.info('Parsed settings from: ' + str(path))
        ex = f"self=id(self) {self}"
        LOG.debug(ex)

        self.save()
        self.locked = False
        self.closing = False
        self.unlockScreen = False

    # Properties

    def get_settings_saved_event(self):
        return self._settings_saved_event

    settings_saved_event = property(get_settings_saved_event)

    # Public methods

    def save(self):
        text = json.dumps(self)
        if self._toxes.has_password():
            text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8')))
        else:
            text = bytes(text, 'utf-8')
        json_path = self._path.replace('.tox', '.json')
        if not json_path:
            #?
            json_path = os.path.join(get_user_config_path(), 'toxygen.json')
        tmp = json_path + str(os.getpid())
        try:
            with open(tmp, 'wb') as fl:
                fl.write(text)
            if os.path.exists(json_path+'.bak'):
                os.remove(json_path+'.bak')
            os.rename(json_path, json_path+'.bak')
            os.rename(tmp, json_path)
        except Exception as e:
            LOG.warn(f'Error saving to {json_path} ' +str(e))
        else:
            self._settings_saved_event(text)

    def close(self):
        path = self._profile_path + '.lock'
        if os.path.isfile(path):
            os.remove(path)

    def set_active_profile(self, profile_path):
        """
        Mark current profile as active
        """
        if not profile_path:
            profile_path = get_auto_profile()

        path = profile_path + '.lock'
        try:
            import shutil
        except:
            pass
        else:
            shutil.copy2(profile_path, path)
            # need to open this with the same perms as _profile_path
            # copy profile_path and then write?
        with open(path, 'wb') as fl:
            fl.write(bytes(str(os.getpid()), 'ascii'))

    def export(self, path):
        text = json.dumps(self)
        name = os.path.basename(self._path)
        with open(join_path(path, str(name)), 'w') as fl:
            fl.write(text)

    def update_path(self, new_path):
        self._path = new_path
        self.save()

    # Static methods

    @staticmethod
    def get_auto_profile(appdir=None):
        if appdir is None:
            appdir = ts.get_user_config_path()
        # self._path =
        p = os.path.join(appdir, 'toxygen.json')
        if not os.path.isfile(p):
            return None
        with open(p) as fl:
            data = fl.read()
        try:
            auto = json.loads(data)
        except Exception as ex:
            LOG.warn(f"json.loads {data}: {ex}")
            auto = {}
        if 'profile_path' in auto:
            path = str(auto['profile_path'])
            if not os.path.isabs(path):
                path = join_path(path, os.path.dirname(os.path.realpath(__file__)))
            if os.path.isfile(path):
                return path
        return None

    @staticmethod
    def supported_languages():
        # backwards
        return supported_languages()

    @staticmethod
    def set_auto_profile(path):
        p = os.path.join(os.path.dirname(path), 'toxygen.json')
        if os.path.isfile(p):
            with open(p) as fl:
                data = fl.read()
            data = json.loads(data)
        else:
            data = {}
        data['profile_path'] = str(path)
        with open(p, 'w') as fl:
            fl.write(json.dumps(data))

    @staticmethod
    def reset_auto_profile():
        appdir = ts.get_user_config_path()
        p = os.path.join(appdir, 'toxygen.json')
        if os.path.isfile(p):
            with open(p) as fl:
                data = fl.read()
            data = json.loads(data)
        else:
            data = {}
        if 'profile_path' in data:
            del data['profile_path']
        with open(p, 'w') as fl:
            fl.write(json.dumps(data))

    @staticmethod
    def get_default_settings(args=None):
        """
        Default profile settings
        """
        retval = {
            # FixMe: match? /var/local/src/c-toxcore/toxcore/tox.h
            'ipv6_enabled': True,
            'udp_enabled': True,
            'trace_enabled': False,
            'local_discovery_enabled': True,
            'dht_announcements_enabled': True,
            'proxy_type': 0,
            'proxy_host': '',
            'proxy_port': 0,
            'start_port': 0,
            'end_port': 0,
            'tcp_port': 0,
            'local_discovery_enabled': True,
            'hole_punching_enabled': False,
            # tox_log_cb *log_callback;
            'experimental_thread_safety': False,
            # operating_system

            'theme': 'default',
            'notifications': False,
            'sound_notifications': False,
            'language': 'English',
            'calls_sound': False, # was True

            'save_history': True,
            'save_unsent_only': False,
            'allow_inline': True,
            'allow_auto_accept': True,
            'auto_accept_path': None,
            'sorting': 0,
            'auto_accept_from_friends': [],
            'paused_file_transfers': {},
            'resend_files': True,
            'friends_aliases': [],
            'show_avatars': False,
            'typing_notifications': False,
            'blocked': [],
            'plugins': [],
            'notes': {},
            'smileys': True,
            'smiley_pack': 'default',
            'mirror_mode': False,
            'width': 920,
            'height': 500,
            'x': 400,
            'y': 400,
            'message_font_size': 14,
            'unread_color': 'red',
            'compact_mode': False,
            'identicons': True,
            'show_welcome_screen': True,
            'close_app': 0,
            'font': 'Times New Roman',
            'update': 0,
            'group_notifications': True,
            'download_nodes_list': False, #
            'download_nodes_url': 'https://nodes.tox.chat/json',
            'notify_all_gc': False,
            'backup_directory': None,

            'audio': {'input': -1,
                      'output': -1,
                      'enabled': True},
            'video': {'device': -1,
                       'width': 320,
                       'height': 240,
                       'x': 0,
                       'y': 0},
            'current_nodes': None,
            'network': 'new',
            'tray_icon': False,
        }
        return retval

    # Private methods

    def _upgrade(self):
        default = Settings.get_default_settings()
        for key in default:
            if key not in self:
                print(key)
                self[key] = default[key]

    def _changed(self, aArgs, info):
        aRet = dict()
        default = Settings.get_default_settings()
        for key in default:
            if key in ['audio', 'video']: continue
            if key not in aArgs.__dict__: continue
            val = aArgs.__dict__[key]
            if val in ['0.0.0.0']: continue
            if key in aArgs.__dict__ and key not in info:
                # dunno = network
                continue
            if key in aArgs.__dict__ and info[key] != val:
                aRet[key] = val
        return aRet

