"""
Name: signApp
Title: 
Author: Cooper
Date: 28/04/2020

Desc:

"""
import os
import time
import serial
import logging

from hanip.onionip import hcp
from hanip.onionip import networkConfigurator
from hanip.onionip.sign import sign_firmloader

class SignApplication(object):
    def __init__(self, config_dict, hw_dict, conf_dir, data_dir):
        self.config_dict = config_dict
        self.hw_dict = hw_dict
        self.config_dir = conf_dir
        self.data_dir = data_dir

        self.sign_graphics = None
        self.setup_graphic_library(self.config_dict.get("SIGN_int_sixteen_high", False))

        comport = self.config_dict.get("SERIAL_unix_comport", "/dev/ttyS1")
        baud = self.config_dict.get("SERIAL_baudrate_host_sign", 38400)
        self.ser = serial.Serial(comport, baud)

        self.network_conf = networkConfigurator.NetworkConfigurator(self.config_dict, self.config_dir)
        self.hcp = hcp.HCP()
        self.sign_firmloader = sign_firmloader.SignFirmloader(self.hw_dict)

    """
    ###################################################################################################################
    Graphics Stuff
    """
    def setup_graphic_library(self, forced_16: False):
        """
        This used to rely on the sign graphics file but as that has not changed in many versions and to simply things
        it is now based in sign task for all modules that import that module.  Whatever additions/changes here should
        be reflected in sign_task if appropriate.

        This is a partial subset only for network related things
        """
        nineteen_high_mode = "-"
        sixteen_high_mode = "+"

        if forced_16:
            logging.info("ST: Internal graphics in 16 high mode")
            mode = sixteen_high_mode
            padding = ""
        else:
            mode = nineteen_high_mode
            padding = "00"

        #CHECK changes sign_task!!!
        graphic_dict = {
            "TRI-UP_ext": r"00{\mode0\mss\at\al\pic\picw3\pich3 070301}",
            "TRI-UP_int": r"00\$MODE\s\g0700$PAD0300$PAD0100$PADg\;".replace("$MODE", mode).replace("$PAD", padding),
            "TRI-DOWN_ext": r"00{\mode0\mss\at\al\pic\picw3\pich3 040607}",
            "TRI-DOWN_int": r"00\$MODE\s\g0400$PAD0600$PAD0700$PADg\;".replace("$MODE", mode).replace("$PAD", padding),
            "ERROR_X_ext": r"00{\mode0\mss\at\al\pic\picw3\pich3 050205}",
            "ERROR_X_int": r"00\$MODE\s\g0500$PAD0200$PAD0500$PADg\;".replace("$MODE", mode).replace("$PAD", padding),
            "NETERR_ext": r"00{\mode0\fit2{{\isp0\al\pic\picw5\pich24 020000020000050000020000050000}}}",
            "NETERR_int": r"00\$MODE\s\g0200$PAD0200$PAD0500$PAD0200$PAD0500$PADg\;".replace("$MODE", mode).replace("$PAD", padding),
            "CLEAR_ext": r"C0",
            "CLEAR_int": r"C0",
        }

        self.sign_graphics = graphic_dict

    def update_sign_display(self, choice):
        """
        Sends the chosen graphic to the sign.  The graphics are stored in the signGraphics.cfg file.
        :param choice: Choice of graphic to display
        """
        try:
            graphic = self.sign_graphics["%s_%s" % (choice,self.hw_dict["hw_type"])]
        except KeyError:
            print("Requested sign graphic doesnt exist", choice, self.hw_dict["hw_type"])
            graphic = "00--"

        self.ser.write(self.hcp.encodeMaster(graphic).encode("latin-1"))


    """
    ###################################################################################################################
    Network Stuff
    """
    def configure_network(self):
        """
        Instructs the network configurator to configure the network and then obtains the IP address.  If it fails to do
        this, then it will call wait_for_network
        """
        if not self.config_dict.get("NETWORK_bypass_link_wait", False):
            self.wait_for_link()

        self.update_sign_display("TRI-UP")
        self.network_conf.set_sign_address(self.hw_dict["address"])     #Network config isnt given a hw_dict so we need to set it
        self.network_conf.configure_network()
        self.network_conf.getIPAddress()

        #A sign should always wait for a network.
        if not self.network_conf.network_status == 0:
            # if self.config_dict["NETWORK_wait_for_network"] or self.network_conf.fallback_enable:
            self.wait_for_network()

    def wait_for_link(self):
        """
        Sits in a loop and waits for the sign to get a network link
        """
        toggle = True

        while 1:
            link_status = self.network_conf.get_link_status()

            if link_status:
                break
            else:
                logging.error("SA: No network link")
                if toggle:
                    self.update_sign_display("NETERR")
                else:
                    self.update_sign_display("CLEAR")

                toggle = not toggle
                time.sleep(1)


    def wait_for_network(self):
        """
        Sits in a loop and waits for the network to be available.  If a timeout has been configured it will break the
        loop once the time has been exceeded and proceeds to set a static IP.  It will also update the sign to show
        the alternating triangles
        :return:
        """
        initial_time = time.time()
        toggle = True

        while 1:
            self.network_conf.getIPAddress()

            if self.network_conf.network_status == 0:
                break
            else:
                if toggle:
                    self.update_sign_display("TRI-UP")
                else:
                    self.update_sign_display("TRI-DOWN")

                toggle = not toggle
                time.sleep(1)

            if self.network_conf.fallback_enable:
                if (time.time() - initial_time) > int(self.config_dict["NETWORK_fallback_timeout"]):
                    self.network_conf.set_static_ip(self.config_dict["NETWORK_static_ip"],
                                                    self.config_dict.get(["NETWORK_subnet_mask"], "255.255.255.0"),
                                                    self.config_dict.get(["NETWORK_gateway"], "0.0.0.0"),
                                                    )
                    break

        self.update_sign_display("TRI-UP")

    """
    ###################################################################################################################
    Main entry point and Mode selector
    """
    def main(self):
        """
        Main entry point, selects the mode according the config file.
        """
        #Check for software update first
        update_status = self.sign_firmloader.check_update_required()

        # Whether this fails or succeeds we want to restart the app
        if update_status == "SUCCESS":
            #At times, the autobaud takes so long to latch the sign thinks the sign is still inop.
            self.sign_firmloader.delete_update_flag()
            return
        elif update_status == "FAIL":
            return

        try:
            self.configure_network()
        except KeyError:
            #A KeyError would imply something missing in the config for the network so deletes the config to put the sign back into factory
            logging.warning("SA: Error setting network, missing parameters, deleting config!")
            os.remove(os.path.join(self.config_dir, "config.cfg"))
            return

        self.hw_dict["unit_IP"] = self.network_conf.ip_address
        self.hw_dict["unit_MAC"] = self.network_conf.mac_address

        if self.hw_dict["serial_number"] == "":
            self.hw_dict["serial_number"] = self.network_conf.mac_address.replace(":", "")

        service_mode = self.config_dict["MODE_service_mode"]
        logging.info("Service mode %s" % service_mode)

        self.ser.close()

        if service_mode == "factory":
            from hanip.onionip.sign import sign_task
            app = sign_task.SignTask(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            app.run_prod_mode()

        elif service_mode == "mqtt":
            from hanip.itxpt import itxpt_mqtt
            app = itxpt_mqtt.ITxPTMQTT(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            app.run()

        elif service_mode == "avms":
            from hanip.itxpt import itxpt_avms
            app = itxpt_avms.Sign_AVMS(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            app.run_sign()

        elif service_mode == "vimi":
            from hanip.itxpt import itxpt_mqtt
            app = itxpt_mqtt.SignMQTT(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            app.run_vimi_mode()

        elif service_mode == "laus":
            from hanip.itxpt import lausanne_mqtt
            app = lausanne_mqtt.Lausanne_SignApp(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            app.run()

        elif service_mode == "ibis-ip":
            from hanip.ibis_ip import ibisipApp
            app = ibisipApp.IBISIPSignApplication(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            app.run()

        elif service_mode == "hanover_mqtt":
            from hanip.itxpt import hanover_mqtt
            mode = hanover_mqtt.HanoverMQTT(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            mode.run()

        elif service_mode == "webcontroller":
            from hanip.web_controller import wifi_controller
            mode = wifi_controller.WebAppSign(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            mode.run()

        elif service_mode == "udp":
            from hanip.onionip.sign import sign_task
            mode = sign_task.SignTask(self.config_dict, self.hw_dict, self.config_dir, self.data_dir)
            mode.run_udp_mode()

        else:
            pass


if __name__ == "__main__":
    pass