"""
Name: mqtt_payload_parser
Title: 
Author: Cooper
Date: 04/10/2023

Desc:  This replaces json_handler and will do much more than dump/load json strings.

The idea of this is that if future MQTT payloads require supporting, as they are only ever going to be text mode
with the exception of Consat and RawHMF, it seems rather daft to make another module/variant to parse text.

So the point of this module is that it can then take any MQTT format and extract the relevant bits from it.
There is no need for this to generate any templates as that would be dealt with by the relevant sign modules

"""
from hanip.onionip import hcp

from typing import Tuple, Optional

import json

class MQTTPayloadParser():
    def __init__(self, config_dict):
        self.config_dict = config_dict
        self.foreground_default = self.config_dict.get("TEMPLATES_default_foreground", "255,255,255")
        # print("DEFAULT FOREGROUND", self.foreground_default)

        self.hcp = hcp.HCP()

    """
    ###################################################################################################################
    General Functions
    """
    def convDictToJSON(self, inputDict: dict, pretty: bool = True) -> str:
        if pretty:
            self.JSONString = json.dumps(inputDict, indent=4, separators=(',', ': '))
        else:
            self.JSONString = json.dumps(inputDict)

        return self.JSONString

    def parseJSON(self, text: str) -> dict:
        try:
            self.jsondata = json.loads(text)
        except json.decoder.JSONDecodeError:
            print("Parsing Error")
            return 1

        return self.jsondata

    def loop_through_dict(self, ipdict: dict, item: str) -> Optional[str]:
        for key, value in ipdict.items():
            if isinstance(value, dict):
                val = self.loop_through_dict(value, item)
                if val != None:
                    return val
            else:
                if key == item:
                    return value
                else:
                    continue

            return None

    """
    ###################################################################################################################
    Format Functions
    """
    def process_message(self, payload: str) -> dict:
        """
        This should be the entry point into this routine, and then return whatever.

        Whatever function this calls to process a given payload, the function must return a tuple containing
        id, command, message anc clear in that given order
        """
        payload_format, payload_dict = self.determine_payload_format(payload)

        if payload_format == None:
            return self.payload_definitions_dict()

        payload_definitions = self.payload_definitions_dict()
        payload_definitions["type"] = payload_format

        if payload_format == "rawhmf":
            message, clear, route_number = self.process_hmf(payload)
            id = command = ""
        elif payload_format == "consat":
            id, command, message, clear, route_number = self.process_consat(payload_dict)

        else:
            parser_name = "parse_%s_payload" % payload_format
            if hasattr(self, parser_name):
                id, command, message, clear = getattr(self, parser_name)(payload_dict)
                route_number = None
            else:
                print("MQTTPP: Non existent parser...")
                id = command = message = ""
                clear = True
                route_number = None

        payload_definitions["id"] = id
        payload_definitions["command"] = command
        payload_definitions["message"] = message
        payload_definitions["clear"] = clear
        payload_definitions["route_number"] = route_number

        return payload_definitions

    def payload_definitions_dict(self) -> dict:
        """

        """
        definitions_dict = {
            "type": None,
            "id": "",
            "command": "",
            "message": "",
            "clear": True,
            "route_number": None
        }

        return definitions_dict

    def determine_payload_format(self, payload: str) -> tuple:
        """
        Whether this is possible or not, with the help of an additional config parameter we can skip the auto detection?
        """
        payload_format = self.config_dict.get("MODE_payload_format", "")
        payload_dict = self.parseJSON(payload)

        if payload_dict == 1:
            return None, None
        else:
            if payload_format == "":
                    try:
                        if payload_dict["type"] == "rawHMF":
                            payload_format = "rawhmf"
                    except KeyError:
                        payload_format = "consat"

        return payload_format, payload_dict

    """
    ###################################################################################################################
    Parser: Hanover and Consat
    """
    def process_hmf(self, payload_str: str) -> tuple:
        """

        """
        hcpMessage = hcp.HMFmsgFromJSON(payload_str)  # This expects a string, not a dict
        message = hcpMessage.extractHMF()
        route_number = hcpMessage.route_number

        if hcpMessage.command == 0 and hcpMessage.body == "\x20":
            clear = True
        else:
            clear = False

        return message, clear, route_number

    def process_consat(self, payload_dict: dict) -> tuple:
        """

        """
        reqid = self.loop_through_dict(payload_dict, "requestID")
        command = self.loop_through_dict(payload_dict, "command")
        message = self.loop_through_dict(payload_dict, "message")
        route_number = self.loop_through_dict(payload_dict, "route_number")

        if command == "C":
            clear = True
        else:
            clear = False

        return reqid, command, message, clear, route_number


    """
    ###################################################################################################################
    Ancillary: Hanover and Consat
    """
    def createConsoleDict(self, hmf, msg, reqId=None, route_number=None):
        """
        Consat format...
        """
        dataDict = {
            "requestId": reqId,
            "requestData": {
                "hanover_hcp": {
                    "command": hmf,
                    "message": msg,
                    "route_number": route_number
                }
            }
        }

        return dataDict

    def createReplyDict(self, hmf, msg, reqId):
        """
        Consat format...
        """
        dataDict = {
            "requestId": reqId,
            "replyData": {
                "hanover_hcp": {
                    "command": hmf,
                    "message": msg,
                }
            }
        }

        return dataDict

    """
    ###################################################################################################################
    Parser: Universal
    """
    def parse_universal_payload(self, payload_dict: dict) -> tuple:
        """
        This is for whatever 3rd party integrator that wants to drive our signs without having to implement superX or other
        """
        if payload_dict == 1:
            return "", "", "", True

        route_number = payload_dict.get("lineNo", "")
        top_line = payload_dict.get("destination", "")
        bottom_line = payload_dict.get("alternativeText", "")
        msg_id = payload_dict.get("messageId", "")

        if bottom_line != "":
            dest_text = top_line + "/" + bottom_line
        else:
            dest_text = top_line

        display_data = {
            "$bcol": "0,0,0",
            "$fcol": "0,0,0",
             "$rn": route_number,
            "$dest": [dest_text]
        }

        command = "0"
        clear = False

        return msg_id, command, display_data, clear

    """
    ###################################################################################################################
    Parser: Storstockholms Lokaltrafik, SL (Stockholm PTA) SR2638
    """
    def parse_storstockholms_payload(self, payload_dict: dict) -> tuple:
        """
        Topic tfbo/dpi/destination_display_text/v1
        // Exempel då det finns en via-destination
        {
        "timestamp": "2020-05-20T07:50:46.380234Z",
        "messageId": "123e4567-e89b-12d3-a456-426614174000",
        "name": "Hornsberg",                            #Mandatory field
        "alternativeText": "via Södersjukhuset",        #Optional field
        "lineDesignation": "72",                        #Optional field
        "initiator": "PTA"
        }
        """

        if payload_dict == 1:
            return "", "", "", True

        route_number = payload_dict.get("lineDesignation", "")
        top_line = payload_dict.get("name", "")
        bottom_line = payload_dict.get("alternativeText", "")
        msg_id = payload_dict.get("messageId", "")

        if bottom_line != "":
            dest_text = top_line + "/" + bottom_line
        else:
            dest_text = top_line

        display_data = {
            "$bcol": "0,0,0",
            "$fcol": self.foreground_default,
             "$rn": route_number,
            "$dest": [dest_text]
        }

        command = "0"
        clear = False

        return msg_id, command, display_data, clear

    def parse_slmqtt_payload(self, payload: dict) -> tuple:
        """
        Just a mirror of the above incase
        """
        return self.parse_storstockholms_payload(payload)


    """
    ###################################################################################################################
    Parser: Ruter 
    """
    def parse_ruter_payload(self, payload_dict: dict) -> tuple:
        """
        Parses the Ruter MQTT payload
        """
        if payload_dict == 1:
            return "", "", "", True

        route_number = payload_dict.get("publicCode", "")
        top_line = payload_dict.get("destination", "")
        bottom_line = payload_dict.get("alternativeText", "")
        if bottom_line == "":
            bottom_line = payload_dict.get("alternativeMessage", "")
        msg_id = payload_dict.get("messageId", "")

        if bottom_line != "":
            dest_text = top_line + "/" + bottom_line
        else:
            dest_text = top_line

        display_data = {
            "$bcol": "0,0,0",
            "$fcol": "0,0,0",
            "$rn": route_number,
            "$dest": [dest_text]
        }

        command = "0"
        clear = False

        return msg_id, command, display_data, clear


    """
    ###################################################################################################################
    Parser: tobs
    tobs is part of the ITxPT MQTT specification Information On Board the Vehicle MQTT Reference Manual Rev RM02v0.3.8 (tobs)
    Telia Journey Information uses a subset of it and is published on tobs/current_destination_display/text
    """

    def parse_tobs_payload(self, payload_dict: dict) -> tuple:
        if payload_dict == 1:
            return "", "", "", True

        route_number = payload_dict.get("lineDesignation", "")
        top_line = payload_dict.get("name", "")
        bottom_line = payload_dict.get("alternativeText", "")
        msg_id = payload_dict.get("messageId", "")

        if bottom_line != "":
            dest_text = top_line + "/" + bottom_line
        else:
            dest_text = top_line

        display_data = {
            "$bcol": "0,0,0",
            "$fcol": "0,0,0",
             "$rn": route_number,
            "$dest": [dest_text]
        }

        command = "0"
        clear = False

        return msg_id, command, display_data, clear

    """
    ###################################################################################################################
    Parser: Copy/Paste this when a new payload is introduced, the name should reflect whatever will be set in the config
    """

    def parse_xxx_payload(self, payload_dict: dict) -> tuple:
        if payload_dict == 1:
            return "", "", "", True

        route_number = payload_dict.get("lineDesignation", "")
        top_line = payload_dict.get("name", "")
        bottom_line = payload_dict.get("alternativeText", "")
        msg_id = payload_dict.get("messageId", "")

        if bottom_line != "":
            dest_text = top_line + "/" + bottom_line
        else:
            dest_text = top_line

        display_data = {
            "$bcol": "0,0,0",
            "$fcol": "0,0,0",
             "$rn": route_number,
            "$dest": [dest_text]
        }

        command = "0"
        clear = False

        return msg_id, command, display_data, clear


if __name__ == "__main__":
    pass
