"""
Name: init_interface
Title: INIT Interface
Author: Niyas Rangath Ummer <niyas@dt.ae>,April 2019
Maintainer: Cooper <cchan@hanoverdisplays.com>

Desc: This is the INIT ISI client, obtains all the relevant data needed for signage
This was originally written by DT as we did not have the means to test the code and still don't.
The code has since been modified to be able to fit into the rest of hanip as such, it is quite happy to run on its own.

 * Copyrights (C) Dubai Technologies L.L.C , Inc - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
"""
import os
import sys
import socket
import time
import json
from json import dumps
import _thread
import logging
import xmltodict

from hanip.itxpt import mqtt_client

sys.path.append(os.path.dirname(os.path.realpath(__file__)))
Appversion="HANIN 1.001A"

class ISISDataItems:
    def __init__(self):
        self.DeviceState = {
            "DeviceState0": {"11, 0, 100"},
            "DeviceState1": {"11, 1, 100"},
            "DeviceState2": {"11, 2, 100"},
            "DeviceState3": {"11, 3, 100"},
            "DeviceState4": {"11, 4, 100"}
        }

        self.isi_dicts = {
                    "AppName": "SIGNBOARD",
                    "CurrentSoftwareVersion": Appversion,
                    "CurrentStop": "",
                    "CurrentStopArabic": "",
                    "Destination": "",  #H
                    "DestinationArabic": "",  #H
                    "DeviceState": self.DeviceState,
                    "LineColor": "",  #H
                    "LineNo": 0,   #H
                    "LineNoForDisplay": 0,
                    "StopDepartureCountdown": "",
                    "NumberOfCameras": "",
                    "Stop1": "",
                    "Stop2": "",
                    "Stop3": "",
                    "Stop4": "",
                    "Stop5": "",
                    "Stop1Arabic": "",
                    "Stop2Arabic": "",
                    "Stop3Arabic": "",
                    "Stop4Arabic": "",
                    "Stop5Arabic": "",
                    "TickerText": "",
                    "IntDispFreeTextInfo": "",
                    "Time_ISO8601": "",
                    "VideoPictureRate": "",
                    "TicketingCicosRaid1": "",
                    "TicketingCicosRaid2": "",
                    "TicketingCicosRaid3": "",
                    "TicketingCicosRaid4": "",
                    "TicketingCicosRaid5": "",
                    "CurrentDirectionNo": 0,
                    "CurrentStopNumber": None,
                    "StopPosition": None,
                    "VehicleNo": 0,
                    "CourseNo": None,
                    "IgnitionState": None,
                    "GPGGA": None,
                    "GPRMC": None,
                    "BlockNo": 0,
                    "DoorState": "",
                    "CurrentStopConnectionInfo": "",
                    "Stop2ConnectionInfo": "",
                    "Stop3ConnectionInfo": "",
                    "Stop4ConnectionInfo": "",
                    "Stop5ConnectionInfo": "",
                    "IsiClientRunsFtpTransfers": 0,
                    "TickerTextArabic": "",
                    "CurrentStopConnectionInfoArabic": "",
                    "Stop2ConnectionInfoArabic": "",
                    "Stop3ConnectionInfoArabic": "",
                    "Stop4ConnectionInfoArabic": "",
                    "Stop5ConnectionInfoArabic": "",
                    "LastStop": "",
                    "LastStopArabic": "",
                    "PositionNearStop": "",
                    "DestinationNo":0,     #HI
                    "DriverId": None,
                    "PatternNo": None,
                    "DutyNumber": None,
                    "TicketingCicosNumberOfTransactionsOnLastStop": None,
                    "TripMode": None,
                    "SerialNumber": "11,abc987654",
                    "CurrentParameterVersion": "HANIN 1.001A",
                    "IsVehicle100mBeforeStopOrAtStop": None,  #HI
                    "GorbaSystemFallbackActive": 0,
                    "BlockDayType": None,
                    "CurrentStopPointPositionNumber": None,
                    "VdvBaseVersion": "",
                    "CurrentTripNo": None,
                    "TicketRejection": None,
                    "LineNameForDisplay": "",    #HI
                    "CurrentStopCode": "",
                    "Stop2Code": "",
                    "Stop3Code": "",
                    "Stop4Code": "",
                    "Stop5Code": "",
                    "StopDepartureCountdownState": None   #HI
                }

        self.hano_in_dicts = {
            "JourneyInfo": {
            "DestinationNo": 0,
            "Destination": "",
            "DestinationArabic": "",
            "LineNameForDisplay": "",
            "LineColor": "",
            "LineNo": 0,
            "StopDepartureCountdownState": None,
            "IsVehicle100mBeforeStopOrAtStop": 0
        },
            "StatusInfo": {
                "CyclicEnable": False,
                "EventEnable": False,
                "Frequency": 0
            }
        }

class INITInterface(object):
    def __init__(self, config_dict):
        self.config_dict = config_dict

        self.ftpupdatestate=0
        self.isireconnectiontimer=0
        self.autopushlist = {
                                'DeviceState'                   :{'Interval': 0, 'Timerval': 0,"Eventflag":0},
                                'IsiClientRunsFtpTransfers'     :{'Interval': 0, 'Timerval': 0,'EventFlag':0}
                            }
        self.isi_data_layer = ISISDataItems()
        self.apprunning_flag = True
        self.init_server_status = False
        self.clientsocket = None
        self.applicaiton_name = 'INFOTAINMENT'
        self.inithostip = "192.168.0.1"
        self.inithostport = 51001
        self.isi_query_timer = 60
        self.brokeraddress = "127.0.0.1"
        self.hano_publishtopic = "isi_journey"
        self.hano_subscribetopic = "isi_status"

        self.load_app_config()
        self.setup_mqtt()
        self.isi_data_layer.isi_dicts["AppName"] = self.applicaiton_name

        logging.info("ISI Appname: %s" % self.isi_data_layer.isi_dicts["AppName"])

    def load_app_config(self):
        """
        Obtains the INIT ISI settings from the config file, if for any reason a parameter is missing then default values
        will be used instead.
        """
        try:
            self.applicaiton_name = self.config_dict.get("ISI_init_appname", "SIGNBOARD")
            self.inithostip = self.config_dict.get("ISI_init_host", "192.168.0.1")
            self.inithostport = int(self.config_dict.get("ISI_init_port", 51001))
            self.isi_query_timer = int(self.config_dict.get("ISI_init_query_timer", 60))

            self.brokeraddress = self.config_dict.get("ISI_broker_address", "127.0.0.1")
            self.hano_publishtopic = self.config_dict.get("ISI_mqtt_isi_topic", "isi_journey")
            self.hano_subscribetopic = self.config_dict.get("ISI_mqtt_status_topic", "isi_status")
        except KeyError:
            pass
        except ValueError:
            pass

    def setup_mqtt(self):
        """
        Sets up the MQTT client
        """
        self.Hano_Interface = mqtt_client.MQTT_Client()
        self.Hano_Interface.set_broker_address("127.0.0.1")
        if self.Hano_Interface.connect_client():
            self.Hano_Interface.subscribe_to_topics([(self.hano_subscribetopic, 0)])
            _thread.start_new_thread(self.Hano_Interface.run_client, ())

    """
    ###################################################################################################################
    ISI Functions
    These functions were written by Niyas, probably worth a rewrite at some point but currently it'll take too long
    to figure out what is going on and rewriting
    """

    def run(self):
        try:
            _thread.start_new_thread(self.manage_isi_connectivity, ())
            _thread.start_new_thread(self.isi_receiver_thread, ())
        except Exception as ex:
            logging.warning("INITIF: Thread did not start: ", ex)

        logging.info("INITIF: Version %s" % Appversion)

        while 1:
            pass

    def check_mqtt_status_payload(self):
        """
        Updates the ISI status dictionary, not currently used yet.
        """
        if self.Hano_Interface.newMsg:
            if self.Hano_Interface.rcvdTopic == self.hano_subscribetopic:
                pass

        #TODO handle status' properly

    def closeisichannel(self):
        try:
            logging.info("INITIF: Manaually closing isi channel connection")
            self.apprunning_flag=False
            self.clientsocket.close()
        except Exception:
            logging.warning("INITIF: Exception in closing isi channel manually")
            pass

    def manage_isi_connectivity(self):
        while self.apprunning_flag:
            try:
                time.sleep(1)
                self.check_isi_connectivity()
                logging.info("INITIF: INIT Status: %s\tMQTT Status: %s" % (self.init_server_status, self.Hano_Interface.brokerConnected))
                if self.init_server_status == False:
                    self.clear_autopush()
                    time.sleep(2)
                    logging.info("INITIF: Opening socket %s:%s" % (self.inithostip, self.inithostport))
                    self.clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    self.clientsocket.connect((self.inithostip, self.inithostport))
                    self.init_server_status = True
                else:
                    try:
                        for key, value in self.autopushlist.items():
                            # print(key,value)
                            if int(self.autopushlist[key]["Interval"]) > 0:
                                # print("autopush interval set for ",key,self.autopushlist[key]["Interval"],self.autopushlist[key]["Timerval"])
                                newtimerval = int(self.autopushlist[key]["Timerval"])
                                newtimerval = newtimerval+1
                                self.autopushlist[key]["Timerval"]=newtimerval
                                if(newtimerval > int(self.autopushlist[key]["Interval"])):
                                    self.autopushlist[key]["Timerval"] = 0
                                    if (key == "DeviceState"):
                                        dicts = self.isi_data_layer.DeviceState
                                        dicts = {'IsiPut': dicts}
                                        xmlString = xmltodict.unparse(dicts, pretty=True)
                                        # print("ISI XML STRING", xmlString)
                                        self.send_isi_packet(xmlString)
                                    if (key == "IsiClientRunsFtpTransfers"):
                                        xmlString = "<IsiPut><IsiClientRunsFtpTransfers>" + str(self.ftpupdatestate) + "</IsiClientRunsFtpTransfers></IsiPut>"
                                        self.send_isi_packet(xmlString)
                    except Exception as ex:
                        logging.warning(ex)
            except Exception as ex:
                logging.warning(ex)
                self.init_server_status = False

    def check_isi_connectivity(self):
        self.isireconnectiontimer = self.isireconnectiontimer+1
        if self.isireconnectiontimer > (self.isi_query_timer+15):
            try:
                self.isireconnectiontimer = 0
                self.init_server_status = False
                self.clientsocket.close()
            except Exception as ex:
                logging.warning(ex)

    def send_isi_packet(self,xmlString):
        try:
            if("DeviceState" in  xmlString):
                xmlString=xmlString.replace("DeviceState0","DeviceState").replace("DeviceState1","DeviceState")\
                    .replace("DeviceState2", "DeviceState").replace("DeviceState3","DeviceState").replace("DeviceState4","DeviceState")
            # print('ISI TX1->>',xmlString)
            self.clientsocket.send((xmlString).encode('ascii'))
        except Exception as ex:
            self.init_server_status = False
            logging.warning(ex)

    def send_isi_string(self, datastring):
        try:
            # print('ISI TX2->>',datastring)
            self.clientsocket.send(datastring.encode('ascii'))
        except Exception as ex:
            self.init_server_status = False
            logging.warning(ex)

    def isi_receiver_thread(self):
        while self.apprunning_flag:
            try:
                if self.init_server_status == True:
                    receivedData = self.clientsocket.recv(4096)
                    self.isireconnectiontimer = 0
                    decoded_input = receivedData.decode("utf8").rstrip()  # decode and strip end of line
                    # print("ISI DECODED-->",decoded_input)
                    self.isi_callback(decoded_input,)
                    if not receivedData:
                        logging.warning("INITIF ISI data reception error disconnecting server")
                        self.init_server_status = False
            except WindowsError as ex:
                self.init_server_status = False
                logging.warning("Reconnection flag set for server reconnection")
            except Exception as ex:
                time.sleep(1)
                #Not sure what other exceptions but seemed originally only do something when there was a WinError
                pass

    def isi_callback(self, data):
        # print("ISI data reception ..... \r\n", data)
        try:
            if("</IsiGet>" in data):
                try:
                    getlist = data.split("</IsiGet>")
                    # print(getlist)
                    if (len(getlist) > 0):
                        for xmllement in getlist:
                            if ("<IsiGet>" in xmllement):
                                xmllement=xmllement+"</IsiGet>"
                                isijsonString=dumps(xmltodict.parse(xmllement))
                                dictionary = json.loads(isijsonString)
                                # print("ISIDICTVAL............", dictionary)
                                lsild = 0
                                # get request parser
                                if 'IsiGet' in dictionary:
                                    # print("ISI GET request received")
                                    mainval = dictionary.get('IsiGet', "")
                                    if 'lsild' in mainval:
                                        lsild = mainval.get('lsild', 0)
                                        # print("Items values", lsild)
                                        if lsild == -1:   # to be checked and verified
                                            self.clear_autopush()

                                    if 'Items' in mainval:
                                        items = mainval.get('Items', "")
                                        # print("Items values", items)
                                        if len(items) > 0:

                                            data = items.split()
                                            if len(data) > 0:
                                                dicts = {}
                                                if ("DeviceState" in items):
                                                    dicts = self.isi_data_layer.DeviceState
                                                else:
                                                    for x in data[:]:
                                                        dicts[x] = self.isi_data_layer.isi_dicts[x]
                                                dicts = {'IsiPut': dicts}
                                                xmlString = xmltodict.unparse(dicts, pretty=True)
                                                # print("ISI XML STRING", xmlString)
                                                self.send_isi_packet(xmlString)

                                    if ("IsiClientRunsFtpTransfers" in items):
                                        time.sleep(1)
                                        sendstring = "<IsiGet><Items>GorbaSystemFallbackActive VehicleNo Time_ISO8601</Items></IsiGet>"
                                        self.send_isi_string(sendstring)

                                    # implementation has to be done later as there is no usecase now
                                    if lsild > 0:
                                        if len(items.count) > 0:
                                            data = items.split()
                                            # print("Dynamic dictionary->", data)

                                    if 'OnChange' in mainval:
                                        onchange = mainval.get('OnChange', "")
                                        indexval = mainval.get('OnChange', "").split()[0]
                                        if (indexval in self.autopushlist):
                                            self.autopushlist[indexval]['Eventflag'] = 1
                                        # print("Updated OnChange values ", onchange)

                                    if 'Cyclic' in mainval:
                                        Cyclic = mainval.get('Cyclic', 0)
                                        indexval = mainval.get('Items', "").split()[0]
                                        if(indexval in self.autopushlist):
                                            self.autopushlist[indexval]['Interval'] = Cyclic
                                        # print('Updated autopush list is ', self.autopushlist)
                except Exception as e:
                        logging.warning("Exception in parsing get loop")

            if ("</IsiPut>" in data):
                putlist = data.split("</IsiPut>")
                if len(putlist) > 0:
                    for xmllement in putlist:
                        if ("<IsiPut>" in xmllement):
                            xmllement = xmllement + "</IsiPut>"
                            isijsonString = json.dumps(xmltodict.parse(xmllement))
                            dictionary = json.loads(isijsonString)
                            # print("Put request from isi dictionary", dictionary, "\r\n", isijsonString)
                            # get request parser
                            if 'IsiPut' in dictionary:
                                mainval = dictionary.get('IsiPut', "")
                                if ('GorbaSystemFallbackActive' in mainval):
                                    #sendstring has to be generated dynamically based on hano_in_dicts dictionary keys
                                    sendstring = "<IsiGet><Items>Destination DestinationArabic LineNo DestinationNo LineNameForDisplay LineColor StopDepartureCountdownState CurrentStop CurrentStopArabic LastStop LastStopArabic IsVehicle100mBeforeStopOrAtStop CurrentStopCode CurrentDirectionNo</Items><OnChange>*</OnChange><Cyclic>"+str(self.isi_query_timer)+"</Cyclic></IsiGet>"
                                    self.send_isi_string(sendstring)
                                else:
                                    isijsonString = isijsonString.replace("IsiPut", "JourneyInfo")
                                    logging.info("INITIF: ISI JSON")
                                    logging.info(isijsonString)
                                    self.Hano_Interface.publish_data(self.hano_publishtopic, isijsonString)
        except Exception as ex:
            print('Error parsing ISI Message: ', ex)

    def clear_autopush(self):
        try:
            for element in self.autopushlist.keys():
                self.autopushlist[element]['Timerval'] = 0
                self.autopushlist[element]['EventFlag'] = 0
            # print(self.autopushlist)
        except Exception as ex:
            logging.warning("INITIF: Cannot clear autopush")


if __name__== "__main__":
    config_dict = {

    }

    InitManager = INITInterface(config_dict)
    _thread.start_new_thread(InitManager.run, ())

    while 1:
        pass