# Copyright 3D Control Systems, Inc. All Rights Reserved 2017-2019.

# Built in San Francisco.

# This software is distributed under a commercial license for personal,
# educational, corporate or any other use.
# The software as a whole or any parts of it is prohibited for distribution or
# use without obtaining a license from 3D Control Systems, Inc.

# All software licenses are subject to the 3DPrinterOS terms of use
# (available at https://www.3dprinteros.com/terms-and-conditions/),
# and privacy policy (available at https://www.3dprinteros.com/privacy-policy/)

import logging
import os
import subprocess
import threading
import config

import paths
from shell_utils import *


class OSUpdater:

    GENERIC_TIMEOUT = 6
    PICAMERA_TIMEOUT = 60

    PACKAGES_NAMES_TO_REMOVE = ['xorg-fonts-alias']
    PACKAGES_NAMES_TO_INSTALL = ['xorg-fonts-alias-100dpi']

    DEFAULT_WIRELESS_REGDOM = 'EE'
    WIRELESS_REGDOM_FILE = '/etc/conf.d/wireless-regdom'
    WIRELESS_REGDOM_LINE = f'WIRELESS_REGDOM="{DEFAULT_WIRELESS_REGDOM}"'
    WIRELESS_REGDOM_COMMAND = f"sudo bash -c echo '{WIRELESS_REGDOM_LINE}' > {WIRELESS_REGDOM_FILE}"
    #WIRELESS_REGDOM_LINE = '''sudo bash -c echo 'WIRELESS_REGDOM="{country_code}"' > /etc/conf.d/wireless-regdom'''
    REMOVE_PACKAGES = 'sudo pacman -Rc --noconfirm'
    LOAD_PRINTEROS_KEY = 'sudo pacman-key -a' + paths.GPG_KEY_FILE
    UPDATE_PACKAGES_DB = 'sudo pacman -Sy'
    UPDATE_ARCHKEYRING = UPDATE_PACKAGES_DB + ' --needed archlinux-keyring'
    CLEAN_CACHE = 'sudo pacman -Scc --noconfirm'
    OS_UPDATE = 'sudo pacman -Syu --noconfirm --overwrite=/boot/*'
    COUNT_PACKS_TO_UPDATE = 'pacman -Qu | wc -l'
    PIP_INSTALL_PICAMERA = 'READTHEDOCS=True pip install -u picamera --no-input --break-system-packages'
    PACKAME_MANAGER_DB_UNLOCK = 'sudo rm /var/lib/pacman/db.lck'

    def __init__(self, app, output_callback):
        self.logger = logging.getLogger(__name__)
        self.app = app
        self.update_process = None
        self.update_thread = None
        self.output_callback = output_callback
        self.apply_wireless_regdom_patch = True

    def remove_db_lock(self):
        output, success = shell_run(self.PACKAME_MANAGER_DB_UNLOCK, timeout=self.GENERIC_TIMEOUT) 

    def perform_update(self, output_callback, at_finish_callback=None):
        self.remove_conflicting_package()
        if self.update_thread and self.update_thread.is_alive():
            self.logger.error('Update thread is already running')
        else:
            self.update_thread = threading.Thread(target=self.update_os_packages)
            self.update_thread.start()
        if at_finish_callback:
            try:
                at_finish_callback()
            except:
                self.logger.exception('Exception in finish callback for OSUpdater.perform_update.')


    def remove_conflicting_package(self):
        for package in self.PACKAGES_NAMES_TO_REMOVE:
            shell_run(self.REMOVE_PACKAGES.format(package_to_remove=package, timeout=self.GENERIC_TIMEOUT))

    def update_os_packages(self):
        try:
            self.update_process = subprocess.Popen(self.OS_UPDATE, stdout=subprocess.PIPE,\
                stderr=subprocess.PIPE, close_fds=True)
        except subprocess.SubprocessError as e:
            self.logger.error('Update subprocess error: ' + str(e))
            try:
                self.update_process.terminate()
            except:
                pass
        else:
            while self.app and not self.app.stop_flag:
                return_code = self.update_process.poll()
                if return_code is not None:
                    self.logger.info('Update process exit with ' + str(return_code))
                    break
                try:
                    line = self.update_process.stdout.readline()
                except Exception as e:
                    self.logger.warning('Exception reading update process stdout:' + str(e))
                    break
                else:
                    if self.output_callback:
                        self.output_callback(line)
                    shell_run(self.REMOVE_PACKAGES)
            self.logger.info("Update subprocess exit")

    def cancel_update(self, kill=False):
        try:
            if not self.update_process:
                self.logger.warning("No update process to cancel")
                return
            if self.update_process.poll() is not None:
                self.logger.warning("Update process already exit")
                return
            if kill:
                self.update_process.kill()
            else:
                self.update_process.terminate()
        except Exception as e:
            self.logger.warning('Exception of update process cancel: ' + str(e))

    def update_install_picamera(self):
        shell_run(self.PIP_INSTALL_PICAMERA, timeout=self.PICAMERA_TIMEOUT)

    def update_pacman_keys(self):
        shell_run(self.UPDATE_ARCHKEYRING)
        if os.path.exists(paths.GPG_KEY_FILE):
            shell_run(self.LOAD_PRINTEROS_KEY, timeout=self.GENERIC_TIMEOUT)

    def get_number_of_packages_to_update(self):
        shell_run(self.UPDATE_PACKAGES_DB)
        output, error = shell_run(self.COUNT_PACKS_TO_UPDATE, timeout=self.GENERIC_TIMEOUT)
        if error:
            self.logger.info(f'Error on getting packages to update: {output} {error}')
        else:
            try:
                return int(output)
            except (TypeError, ValueError):
                error = 'Got invalid output on number of packages to update: ' + str(output)
                self.logger.warning(error)

    def patch_wireless_regdom(self):
        self.logger.info("Writing wireless regdom settings")
        command = self.WIRELESS_REGDOM_COMMAND.format(country_code = '" ' + str(config.get_settings().get('wifi_regdom', 'GB')) + ' "')
        shell_run(command)
        self.logger.info("Wireless regdom file write complete")

    #  def check_and_write_wireless_regdom(self):
    #      regdom_line = self.WIRELESS_REGDOM_LINE.replace(country_code = self.country_code)
    #      apply_patch = False
    #      try:
    #          with open(self.WIRELESS_REGDOM_FILE) as f:
    #              lines = f.readlines()
    #      except OSError:
    #          apply_patch = True
    #      else:
    #          if not lines or lines[-1] != regdom_line:
    #              apply_patch = True
    #      if apply_patch and self.apply_wireless_regdom_patch:
    #          self.patch_wireless_regdom()
                


if __name__ == "__main__":
    import sys
    args = sys.argv
    os_updater = OSUpdater(None, None)
    if "--wireless_regdom":
        os_updater.patch_wireless_regdom()
