#!/usr/bin/python3
# -*- coding: utf-8 -*-

import argparse
import time
import sys

from concurrent.futures import ThreadPoolExecutor

from lxcmgr import lxcmgr
from lxcmgr.pkgmgr import App, Stage, PkgMgr

def print_apps(packages):
    for app, meta in packages.items():
        print(app, meta['version'])
        for key, value in meta['meta'].items():
            print('  {}: {}'.format(key, value))

def list_online():
    pm = PkgMgr()
    apps = pm.online_packages['apps']
    if apps:
        print_apps(apps)
    else:
        print('Repository lists no applications packages.')

def list_installed():
    pm = PkgMgr()
    apps = pm.installed_packages['apps']
    if apps:
        print_apps(apps)
    else:
        print('No applications packages installed.')

def list_updates():
    pm = PkgMgr()
    apps = pm.installed_packages['apps']
    if apps:
        updateable_apps = [app for app in apps if pm.has_update(app)]
        if updateable_apps:
            updates = {name: meta for (name, meta) in pm.online_packages['apps'].items() if name in updateable_apps}
            print_apps(updates)
        else:
            print('All installed application packages are up-to-date.')
    else:
        print('No applications packages installed.')

def run_install_action(action, app):
    with ThreadPoolExecutor() as executor:
        future = executor.submit(action, app)
        while not future.done():
            time.sleep(0.25)
            print_install_status(app)
        # Get the result of the future and let it raise exception, if there was any
        data = future.result()
    print_install_status(app)

def print_install_status(app):
    # Prints current status of the installation. Uses ANSI "erase line" and "carriage return" to rewrite the status on single line.
    if app.stage == Stage.QUEUED:
        print('\x1b[KQueued...', end='\r')
    elif app.stage == Stage.DOWNLOAD:
        print('\x1b[KDownloading... {} % ({} / {} bytes)'.format(app.percent_processed, app.bytes_processed, app.bytes_total), end='\r')
    elif app.stage == Stage.UNPACK:
        print('\x1b[KUnpacking...', end='\r')
    elif app.stage == Stage.INSTALL:
        print('\x1b[KInstalling...', end='\r')
    elif app.stage == Stage.UNINSTALL:
        print('\x1b[KUninstalling...', end='\r')
    elif app.stage == Stage.UPDATE:
        print('\x1b[KUpdating...', end='\r')
    elif app.stage == Stage.DONE:
        print('\x1b[KDone.')

def install_app(app):
    pm = PkgMgr()
    app = App(app)
    run_install_action(pm.install_app, app)

def update_app(app):
    pm = PkgMgr()
    app = App(app)
    run_install_action(pm.update_app, app)

def uninstall_app(app):
    pm = PkgMgr()
    app = App(app)
    run_install_action(pm.uninstall_app, app)

def main(args):
    if args.action == 'list-installed':
        list_installed()
    elif args.action == 'list-online':
        list_online()
    elif args.action == 'list-updates':
        list_updates()
    elif args.action == 'install':
        install_app(args.app)
    elif args.action == 'update':
        update_app(args.app)
    elif args.action == 'uninstall':
        uninstall_app(args.app)

parser = argparse.ArgumentParser(description='LXC container and package manager')
subparsers = parser.add_subparsers()

parser_list = subparsers.add_parser('list')
subparsers_list = parser_list.add_subparsers()
parser_list_installed = subparsers_list.add_parser('installed')
parser_list_installed.set_defaults(action='list-installed')
parser_list_online = subparsers_list.add_parser('online')
parser_list_online.set_defaults(action='list-online')
parser_list_updates = subparsers_list.add_parser('updates')
parser_list_updates.set_defaults(action='list-updates')

parser_install = subparsers.add_parser('install')
parser_install.set_defaults(action='install')
parser_install.add_argument('app', help='Application to install')

parser_update = subparsers.add_parser('update')
parser_update.set_defaults(action='update')
parser_update.add_argument('app', help='Application to update')

parser_uninstall = subparsers.add_parser('uninstall')
parser_uninstall.set_defaults(action='uninstall')
parser_uninstall.add_argument('app', help='Application to uninstall')

main(parser.parse_args())
