# -*- coding: utf-8 -*-


from os.path import join, exists, dirname
import os
import shutil
import signal
import subprocess
import sys

import settings
from utils import run, get

root_dir = dirname(settings.base_dir)

HIDDEN = ['install_update', 'install_launcher', 'uninstall_launcher']

def r(*cmd):
    return subprocess.call(cmd)

def version(module):
    if os.path.exists(join(root_dir, module, '.git')):
        os.chdir(join(root_dir, module))
        version = get('git', 'log', '-1', '--format=%cd', '--date=iso').split(' ')[0].replace('-', '')
        version += '-' + get('git', 'rev-list', 'HEAD', '--count').strip()
        version += '-' + get('git', 'describe', '--always').strip()
        os.chdir(root_dir)
    else:
        if 'modules' in settings.release and module in settings.release['modules']:
            version = settings.release['modules'][module]['version']
        else:
            version = -1
    return version

def command_version(*args):
    """
        Print current version
    """
    print(version('openmedialibrary'))

def command_debug(*args):
    """
        Start in debug mode
    """
    pass

def command_start(*args):
    """
        Start Open Media Libary
    """
    pass

def command_stop(*args):
    """
        Stop Open Media Libary
    """
    if sys.platform == 'win32':
        from utils import check_pid
        if args:
            pid = args[0]
        else:
            pid = os.path.join(settings.data_path, 'openmedialibrary.pid')
        try:
            with open(pid) as fd:
                pid = int(fd.read())
        except:
            return
        if check_pid(pid):
            os.kill(pid, signal.SIGTERM)

def command_restart(*args):
    """
        Restart Open Media Libary
    """
    if sys.platform == 'win32':
        from urllib.request import urlopen
        from urllib.parse import urlencode
        url = 'http://{address}:{port}/api/'.format(**settings.server)
        data = urlencode({'action': 'quit', 'data': '{}'}).encode()
        try:
            urlopen(url, data, timeout=2)
        except:
            pass
        from utils import check_pid, ctl
        pidfile = os.path.join(settings.data_path, 'openmedialibrary.pid')
        try:
            with open(pidfile) as fd:
                pid = int(fd.read())
        except:
            pid = None
        if pid and check_pid(pid):
            os.kill(pid, signal.SIGTERM)
        ctl('server', pidfile)

def command_install_launcher(*args):
    """
        Install launcher
    """
    import integration
    integration.install_launcher()

def command_uninstall_launcher(*args):
    """
        Uninstall launcher
    """
    import integration
    integration.uninstall_launcher()

def command_install_update(*args):
    """
        Install available updates
    """
    pass

def command_update(*args):
    """
        Update to latest development version
    """
    import update
    print('Downloading...')
    if not update.download():
        print('Download failed')
        return
    print('Installing...')
    if not update.install():
        print("Installation failed")

def command_postupdate(*args):
    """
        Called after update with -o old -n new
    """
    o, old, n, new = args
    if o != '-o' or n != '-n':
        print('usage: -o oldversion -n newversion')
        sys.exit(1)
    if old <= '20140521-65-e14c686' and new > '20140521-65-e14c686':
        if not os.path.exists(settings.db_path) and sys.platform != 'win32':
            r('./ctl', 'setup')
    import setup
    setup.upgrade_db(old, new)

def command_setup(*args):
    """
        Setup new node
    """
    import setup
    setup.create_db()

def command_update_static(*args):
    """
        Update static files
    """
    import setup
    import utils
    setup.create_db()
    old_oxjs = os.path.join(settings.static_path, 'oxjs')
    oxjs = os.path.join(settings.base_dir, '..', 'oxjs')
    if os.path.exists(old_oxjs) and not os.path.exists(oxjs):
        shutil.move(old_oxjs, oxjs)
    if not os.path.exists(oxjs):
        r('git', 'clone', '--depth', '1', 'https://code.0x2620.org/0x2620/oxjs.git', oxjs)
    elif os.path.exists(os.path.join(oxjs, '.git')):
        os.system('cd "%s" && git pull' % oxjs)
    r('python3', os.path.join(oxjs, 'tools', 'build', 'build.py'), '-nogeo')
    utils.update_static()
    reader = os.path.join(settings.base_dir, '..', 'reader')
    if not os.path.exists(reader):
        r('git', 'clone', '--depth', '1', 'https://code.0x2620.org/0x2620/openmedialibrary_reader.git', reader)
    elif os.path.exists(os.path.join(reader, '.git')):
        os.system('cd "%s" && git pull' % reader)

def command_release(*args):
    """
        Release new version
    """
    print('checking...')
    import base64
    import ed25519
    import hashlib
    import json
    import OpenSSL.crypto
    from OpenSSL.crypto import load_privatekey, FILETYPE_PEM
    import os

    release_name = args[0] if args else 'release'

    os.chdir(root_dir)
    with open(os.path.expanduser('~/.openmedialibrary_release.key'), 'rb') as fd:
        SIG_KEY=ed25519.SigningKey(fd.read())
    SIG_ENCODING='base64'
    with open(os.path.expanduser('~/.openmedialibrary_tls_release.key'), 'rb') as fd:
        tls_key = load_privatekey(FILETYPE_PEM, fd.read())

    def sign(release):
        value = []
        for module in sorted(release['modules']):
            value += ['%s/%s' % (release['modules'][module]['version'], release['modules'][module]['sha1'])]
        value = '\n'.join(value).encode()
        sig = SIG_KEY.sign(value, encoding=SIG_ENCODING).decode()
        release['signature'] = sig
        digest = 'sha1'
        tls_sig = OpenSSL.crypto.sign(tls_key, value, digest)
        release['signature_%s'%digest] = base64.b64encode(tls_sig).decode()
        import update
        if not update.verify(release):
            print('verifiying signature failed!')
            return False
        return True

    def sha1sum(path):
        h = hashlib.sha1()
        with open(path, 'rb') as fd:
            for chunk in iter(lambda: fd.read(128*h.block_size), b''):
                h.update(chunk)
        return h.hexdigest()

    MODULES = ['platform', 'openmedialibrary', 'oxjs', 'reader']
    PLATFORM_MODULES = {
        'platform_linux32': 'linux32',
        'platform_linux64': 'linux64',
        'platform_linux_armv7l': 'linux_armv7l',
        'platform_linux_aarch64': 'linux_aarch64',
        'platform_darwin64': 'darwin64',
        'platform_win32': 'win32',
    }
    MODULES += list(PLATFORM_MODULES)
    VERSIONS = {module:version(module) for module in MODULES}

    EXCLUDE = [
        '--exclude', '.git', '--exclude', '.bzr',
        '--exclude', 'pip_cache',
        '--exclude', '.*.swp', '--exclude', '._*', '--exclude', '.DS_Store',
        '--exclude', 'Geo/png'
    ]

    #run('./ctl', 'update_static')
    for module in MODULES:
        tar = join('updates', '%s-%s.tar.bz2' % (module, VERSIONS[module]))
        if not exists(tar):
            cmd = ['tar', 'cvjf', tar] + EXCLUDE
            if module in ('openmedialibrary', ):
                cmd += ['--exclude', '*.pyc']
            if module == 'openmedialibrary':
                cmd += ['--exclude', 'gunicorn.pid']
            if module == 'oxjs':
                cmd += ['--exclude', 'oxjs/examples']
            if module == 'platform_win32':
                cmd += ['--exclude', 'build', '--exclude', 'dist']
            cmd += [module + '/']
            run(*cmd)
    release = {}
    release['modules'] = {module: {
        'name': '%s-%s.tar.bz2' % (module, VERSIONS[module]),
        'version': VERSIONS[module],
        'sha1': sha1sum(join('updates', '%s-%s.tar.bz2' % (module, VERSIONS[module])))
    } for module in MODULES}
    for module in PLATFORM_MODULES:
        release['modules'][module]['platform'] = PLATFORM_MODULES[module]
    if sign(release):
        with open('updates/%s.json' % release_name, 'w') as fd:
            json.dump(release, fd, indent=2, sort_keys=True)
        print('signed latest release in updates/%s.json' % release_name)

def command_shell(*args):
    '''
        Runs a Python shell inside the application context.
    '''
    context = None
    banner = 'Open Media Library'

    import db
    with db.session():
        # Try BPython
        try:
            from bpython import embed
            embed(banner=banner, locals_=context)
            return
        except ImportError:
            pass

        # Try IPython
        try:
            try:
                # 0.10.x
                from IPython.Shell import IPShellEmbed
                ipshell = IPShellEmbed(banner=banner)
                ipshell(global_ns=dict(), local_ns=context)
            except ImportError:
                # 0.12+
                from IPython.terminal.ipapp import TerminalIPythonApp
                app = TerminalIPythonApp.instance()
                app.extensions = ['autoreload']
                app.exec_lines = ['%autoreload 2']
                app.display_banner = False
                print(banner)
                app.initialize(argv=[])
                app.start()
            return
        except ImportError:
            pass

        import code
        # Use basic python shell
        code.interact(banner, local=context)

def command_dump_json(*args):
    """
        Dump items to json
    """
    if not args:
        print('usage: ./ctl json_dump dump.json')
        sys.exit(1)
    import db
    import state
    with db.session():
        library = state.user().library
        library.export_json(args[0])

def main():
    actions = globals()
    commands = [c[8:] for c in actions if  c.startswith('command_')]
    command = sys.argv[1] if len(sys.argv) > 1 else None
    if command and command in commands:
        globals()["command_%s"%command](*sys.argv[2:])
    else:
        print("usage: ./ctl [action]")
        indent = max([len(command) for command in commands]) + 4
        for command in sorted(commands):
            if command in HIDDEN:
                continue
            space = ' ' * (indent - len(command))
            info = actions["command_%s"%command].__doc__.split('\n')
            info = ['  %s%s' % (' ' * indent, i.strip()) for i in info]
            info = '\n'.join(info).strip()
            print(("  %s%s%s" % (command, space, info)))
        sys.exit(1)