#!/usr/bin/env python3
import os
import ast
import time
import json
import pickle
import random
import threading
import netifaces as ni
import fire

from server.Thing import Thing
from lib.usb.ether import Ether
from lib import systemUtils as utils
from lib.usb.keyboard import Keyboard
import netifaces as ni


class MainApp:
    def __init__(self, appDir="/etc/BSc", configName="config", payloadsFile="payloads"):
        utils.log("START", module=self.__class__.__name__)
        self._device = None
        self._thing = None
        MainApp.APP_DIR = appDir
        MainApp.CONFIG_NAME = configName
        MainApp.PAYLOADS_FILE = payloadsFile

    def _connect(self):
        utils.log("CONNECT", module=self.__class__.__name__)
        with open(os.path.join(MainApp.APP_DIR, MainApp.CONFIG_NAME), 'r') as f:
            externalID = f.read()
            self._thing = Thing(
                f'"{externalID}"',
                f'"{externalID}"',
                "http://165.22.121.233:8888/api/authorization/",
                "165.22.121.233"
            )
            self._thing.on_message = MainApp.on_message
            self._thing.Start()
            self._thr = threading.Thread(target=self._thing.OnConnected)
            self._thr.start()
            utils.log(f"connected with external ID: {externalID}", module=self.__class__.__name__)

    def launch(self):
        utils.log("LAUNCH", module=self.__class__.__name__)
        utils.getRootPrivileges()
        self._connect()
        ni.ifaddresses('wlan0')
        ip = ni.ifaddresses('wlan0')[ni.AF_INET][0]['addr']
        print(ip)
        try:
            with open(os.path.join(MainApp.APP_DIR, MainApp.PAYLOADS_FILE), "rb+") as f:
                data = pickle.load(f)
            os.remove(os.path.join(MainApp.APP_DIR, MainApp.PAYLOADS_FILE))
            payloads = data.pop("payloads")
            deviceType = data.pop("deviceType").upper()

            if deviceType == "KEYBOARD":
                self._device = Keyboard(thing=self._thing, **data)
            elif deviceType == "ETHER":
                self._device = Ether(
                    bckPath=os.path.join(MainApp.APP_DIR, "bck"),
                    thing=self._thing,
                    **data
                )
            else:
                raise RuntimeError(f"Unkonwn deviceType: {deviceType}")

            for payload in payloads:
                self._device.executePayload(**payload)

        except Exception as e:     # any exception during runtime
            utils.log(e, flag="DEBUG", module=self.__class__.__name__)
            self._thing.Send(f"ERROR:{e}")
            data = None

        finally:
            if self._device is not None:
                self._device.tearDown()
            utils.log("waiting for payloads", module=self.__class__.__name__)
            while not os.path.exists(os.path.join(MainApp.APP_DIR, MainApp.PAYLOADS_FILE)):
                time.sleep(5)

            utils.log("REBOOT", module=self.__class__.__name__)
            os.system("sudo reboot")

    def setup(self, apt=True):
        utils.getRootPrivileges()
        os.makedirs(MainApp.APP_DIR, exist_ok=True)

        with open(os.path.join(MainApp.APP_DIR, MainApp.CONFIG_NAME), 'w') as f:
            utils.log("Writing intial data", module=self.__class__.__name__)
            f.write(str(random.randint(1000, 9999)))

        with open("/boot/config.txt", 'r') as f:
            bootOk = "dtparam=spi=on\ndtoverlay=dwc2" in f.read()

        if not bootOk:
            with open("/boot/config.txt", 'a') as f:
                utils.log(
                    "Modyfing /boot/config.txt",
                    module=self.__class__.__name__
                )
                f.write("dtparam=spi=on\ndtoverlay=dwc2\n")

        if apt:
            utils.aptInstall("dnsmasq", "tcpdump")

        utils.log("SETUP END", module=self.__class__.__name__)

    @staticmethod
    def on_message(thing, userdata, message):
        message = str(message.payload.decode("utf-8"))
        data = ast.literal_eval(message)
        with open(os.path.join(MainApp.APP_DIR, MainApp.PAYLOADS_FILE), 'wb') as f:
            utils.log("Writing payload to file", module="ON MESSAGE")
            pickle.dump(data, f)

if __name__ == "__main__":
    fire.Fire(MainApp)
