"""
Name: isi_msg_handler
Title: 
Author: Cooper
Date: 24/04/2019

Desc: The nature of the messages from the ISI protocol on paper are pretty simple, get some parameters, parse/jiggle them
as necessary and then send to sign.

This class will not do any data grabbing, it will just be given some data and will process it depending on a set of
non existent rules... Okay I lied, make more sense for this to sort it all out for the main app to display so it will
return a list of stuff that is sent to the signs.

So there are three scenarios:
1) Manual control via front panel
2) Remote control via ISI, receiving a dest code
3) Remote control via ISI, receiving display text

The plan is to look at the local code, if thats not zero, use it, otherwise...
look at the remote code, if thats not zero use it, otherwise...
Finally look at the isi text and use that.

If all three are missing then display "."

Wham bam thank you ma'am
"""
import os

class ISIMessageHandler(object):
    def __init__(self, config_dict, data_loc):
        self.config_dict = config_dict
        self.database_path = os.path.join(data_loc, "payload")

        self.isi_dict = None
        self.sign_dict = None

        self.remote_set_code = False
        self.remote_set_text = False
        self.prev_remote_code = None
        self.console_display_text = "..."

    """
    TEMPLATE HANDLING STUFF
    """
    def convert_colour(self, colour):
        """
        Converts a standard RGB value into a Hanover BGR value because it adds an element of chaos to the general
        scheme of things

        Parameters:
            colour (string): comma separated RGB value.  Will accept empty string, "None" or None

        Returns:
             col_val (string): The Hanover equivalent colour value
        """
        if colour == "":
            colour = "0,0,0"
        if colour == None or colour == "None":
            colour = "0,0,0"

        r, g, b = colour.split(",")

        r = int(r)
        g = int(g) << 8
        b = int(b) << 16

        col_val = b + g + r

        return str(col_val)

    def generate_colour_command(self, fc, bc):
        """
        Takes a foreground, and background RGB value and converts it into a series of appropriate SuperX colour commands
        It uses the the colour template stored in the config.cfg file to paste values into

        Parameters:
            fc (string): Foreground colour
            bc (string): Background colour

        Returns:
            colour_command (string): SuperX command containing the foreground and background colours
        """
        fore = self.convert_colour(fc)
        back = self.convert_colour(bc)

        colour_command = self.config_dict["TEMPLATES_colour"].replace("$fore", fore).replace("$back", back)

        return colour_command

    def process_template_items(self):
        """
        This parses through the config.cfg [TEMPLATES] section and looks for anything that is prefixed with "$", then
        separates what is stored at that given key via ;.

        If there is more than one value after the split, then it will go through each value in turn.  If the value is
        prefixed with a caret (^) then it will use the value that is stored in the config.cfg, otherwise it will obtain
        it from the ISI dict.

        Finally, it is added to the dictionary, where the key is whatever is left after TEMPLATeS_$ is stripped

        Parameters:
            None

        Returns:
            template_items (dict):  Dictionary containing the ISI items that the config.cfg value instructs to obtain

        """
        template_items = {}

        for (key, item) in self.config_dict.items():
            if "TEMPLATES_$" in key:
                # print(key, item)
                # if item == None or item == "None":
                #     template_items[key[10:]] = ""
                #     continue

                values = item.split(";")

                if len(values) > 1:
                    temp = []
                    for value in values:
                        if "^" in value:
                            #Strip off the caret and append to temp
                            temp.append(value[1:])
                        else:
                            temp.append(self.get_isi_journey_item(value))

                    #Add temp list to dictionary, the key is whatever is left after TEMPLATES_$ is stripped
                    template_items[key[10:]] = temp

                else:
                    if "^" in item:
                        #Strip off the caret and add to dict, the key is whatever is left after TEMPLATES_$ is stripped
                        template_items[key[10:]] = item[1:]
                    else:
                        template_items[key[10:]] = self.get_isi_journey_item(item)


        print("Template items: ", template_items)
        return template_items

    def generate_templates(self):
        """
        This routine generates all possible combinations of sign messages, both monochrome and colour route number in
        SuperX modes 0 to 3.

        It calls the other routines to obtain:
        - ISI template items, such as the destination in both languages, colour and line number
        - The SuperX colour header

        Once all the necessary information is gathered including parameters stored in the config.cfg, it will generate
        two sets of lists containing three templates.  Where the list index corresponds to the follow sign type:

        0: Mode 3 capable signs e.g. Large front signs
        1: Mode 1 capable signs e.g. Single line side signs
        2: Mode 0 capable signs e.g. Route numbers in general

        Parameters:
            None

        Returns:
            generated_templates_mono (list): A list containing the monochrome sign templates with the variables filled in
            generated_templates_col (list): A list containing the colour sign templates with the variables filled in
        """
        generated_templates_col = [""]*3
        generated_templates_mono = [""]*3
        #Where generated_templates[0] are mode3 capable signs, [1] are mode 1 capable and [2] are mode0 only
        template_items = self.process_template_items()

        alignment = self.config_dict["TEMPLATES_rn_just"]
        if alignment == "right":
            align = r"\rh"
        else:
            align = ""

        route_number = str(template_items["$rn"])
        if len(route_number) > 0:
            rn_flag = True
        else:
            rn_flag = False

        col = self.generate_colour_command(template_items["$fcol"], template_items["$bcol"])

        #The delimiter is what splits the text into two lines for Mode 2 and Mode 3 messages
        delimiter = self.config_dict["TEMPLATES_delimiter"]

        #TODO: I think we would need to smuggle the countdown stuff inside the template_items["$dest"] this means that
        # it can go through the same process as the normal dests.
        try:
            countdown_state = template_items["$departure"]
        except KeyError:
            pass
        else:
            countdown_messages = self.get_countdown_state_messages(countdown_state)

            if countdown_messages != None:
                template_items["$dest"].append(countdown_messages[0])
                template_items["$dest"].append(countdown_messages[1])

        print(template_items["$dest"])

        for destination_text in template_items["$dest"]:
            dest = destination_text

            if delimiter in destination_text:
                two_line = True
                split_dests = dest.split(delimiter)
                dest_0 = dest.replace(delimiter, " ")
                dest_1 = split_dests[0]
                dest_2 = split_dests[1]
            else:
                two_line = False
                dest_0 = dest


            #We need to generate data for the three sign types, two line, single line and RN
            mode_zero = self.config_dict["TEMPLATES_mode0"]
            mode_one = self.config_dict["TEMPLATES_mode1"]
            mode_two = self.config_dict["TEMPLATES_mode2"]
            mode_three = self.config_dict["TEMPLATES_mode3"]

            if rn_flag:
                generated_templates_col[0] = mode_zero.replace("$rn", str(route_number))
                generated_templates_col[1] += mode_one.replace("$rn", str(route_number))\
                    .replace("$dest", dest_0)
                if two_line:
                    generated_templates_col[2] += mode_three\
                        .replace("$rn", route_number)\
                        .replace("$dest_1", dest_1)\
                        .replace("$dest_2", dest_2)
                else:
                    generated_templates_col[2] += mode_one\
                        .replace("$rn", route_number)\
                        .replace("$dest", dest)
            else:
                generated_templates_col[0] = mode_zero.replace("$rn", " ")
                generated_templates_col[1] += mode_zero.replace("$rn", dest_0)
                if two_line:
                    generated_templates_col[2] += mode_two\
                        .replace("$dest_1", dest_1)\
                        .replace("$dest_2", dest_2)
                else:
                    generated_templates_col[2] += mode_zero.replace("$rn", dest_0)

        for idx, gen_temp in enumerate(generated_templates_col):
            generated_templates_mono[idx] = generated_templates_col[idx] \
                .replace("$al", align) \
                .replace("$col", "")
            generated_templates_col[idx] = generated_templates_col[idx]\
                .replace("$al", align)\
                .replace("$col", col)

        # print(generated_templates)
        return generated_templates_mono, generated_templates_col

    """
    ISI HANDLING STUFF
    """
    def update_isi_dict(self, isi_dict):
        """
        Just allows the importer to update the ISI dict that this module uses.  It is set as a global because it is
        used by a lot of subroutines
        """
        self.isi_dict = isi_dict

    def get_countdown_state_messages(self, state):
        """
        This determines the state of the countdown via the value provided by the INIT CoPilot.  This value is then used
        to display the appropriate time on the sign(s).

        Taken from the 2.2.14.11_ISI Data Spec:
            0 - No departure information available or departure time was passed.
            1 - “Departure within 10 min”
            2 - “Departure in 5 min”
            3 - “Departure in 4 min”
            4 - “Departure in 3 min”
            5 - “Departure in 2 min”
            6 - “Departure in 1 min“
            7 - “Departure in less than 1 min”

        Parameters:
            state (string): State of the countdown departure as obtained via the ISI protocol
        """
        countdown_state_en = {
            1: "Departure within/10 min",
            2: "Departure in/5 min",
            3: "Departure in/4 min",
            4: "Departure in/3 min",
            5: "Departure in/2 min",
            6: "Departure in/1 min",
            7: "Departing now"
        }

        countdown_state_ar = {
            1: "المغادرة خلال/10 دقائق",
            2: "المغادرة خلال/5 دقائق",
            3: "المغادرة خلال/4 دقائق",
            4: "المغادرة خلال/3 دقائق",
            5: "المغادرة خلال/2 دقائق",
            6: "المغادرة خلال/1 دقائق",
            7: "المغادرة الآن"
        }

        try:
            state = int(state)
        except ValueError:
            return None

        if state == 0:
            return None
        else:
            return countdown_state_ar[state], countdown_state_en[state]

    def get_isi_test_mode(self):
        """

        """
        try:
            remote_test_mode = self.isi_dict["StatusInfo"]["TestMode"]
        except KeyError:
            remote_test_mode = False

        return remote_test_mode

    def get_isi_status_paramters(self):
        """
        This obtains all status items
        """
        try:
            status_frequency = int(self.isi_dict["StatusInfo"]["Frequency"])
            enable_cyclic_status = self.isi_dict["StatusInfo"]["CyclicEnable"]
            enable_event_status = self.isi_dict["StatusInfo"]["EventEnable"]
        except ValueError:
            return None
        except KeyError:
            return None

        return (status_frequency, enable_cyclic_status, enable_event_status)

    def get_isi_journey_item(self, item, padd=False):
        """
        Attempts to obtain the ISI data item requested

        Parameters:
            item (string): The ISI data item to be obtained

        Returns:
            value (string): The value if the ISI data item if valid, otherwise an empty string
        """
        try:
            value = self.isi_dict["JourneyInfo"][item]

            if value == None or value == "None":
                value = ""
        except KeyError:
            print("No key for %s" % item)
            value = ""

        if padd:
            #No idea why it is padded with no zeros but leaving here
            value = str(value).zfill(0)

        return value

    def process_isi_text(self, sign_dict):
        """
        This routine is responsible for providing the data that is to be displayed on the signs.  It takes in the
        sign_dict from the signDiscovery module, calls the routine to generate all the necessary templates and correlates
        whats fitted on a vehicle to what has been generated using some basic assumptions:

        If a sign is less than 11 pixels high, it is a single row sign.
        If a sign is less than 41 pixels wide, it is a route number sign
        Otherwise it is a full destination sign

        This routine will also correct the route number width for colour signs, if what has been generated is not the
        same as what has been detected.

        If there is no data for a given sign address, the data will be a "." as that is more helpful than a blank sign

        Parameters:
            sign_dict (dict):  A dictionary containing signs for all valid HCP addresses, if there is no data for a
            given sign then it have None as it's data

        Returns:
            sign_data (list): A list of all the sign data, where the index corresponds to the HCP address
        """
        if self.isi_dict == None:
            return ["."] * 14

        self.console_display_text = "%s %s" % (self.isi_dict["JourneyInfo"]["LineNameForDisplay"], self.isi_dict["JourneyInfo"]["Destination"])

        sign_data = []
        filled_templates_mono, filled_templates_col = self.generate_templates()

        for sign in sign_dict:
            if sign_dict[sign] != None:
                sign_resolution = sign_dict[sign]["sign_size"]
                try:
                    col_resolution = sign_dict[sign]["colour_panel"]
                except KeyError:
                    col_resolution = None
                sign_w, sign_h = sign_resolution.split("x")
                # print(sign, sign_w, sign_h)

                if col_resolution == None:
                    if int(sign_h) < 11:
                        # Single line signs: Mode 0,1
                        temp_sign_data = filled_templates_mono[1]

                    elif int(sign_w) < 41:
                        # Route number only: Mode 0
                        temp_sign_data = filled_templates_mono[0]
                    else:
                        # RN two line: Mode 0,1,2,3
                        temp_sign_data = filled_templates_mono[2]

                    temp_sign_data =temp_sign_data.replace("$rw", "")
                else:
                    if int(sign_h) < 11:
                        # Single line signs: Mode 0,1
                        temp_sign_data = filled_templates_col[1]

                    elif int(sign_w) < 41:
                        # Route number only: Mode 0
                        temp_sign_data = filled_templates_col[0]
                    else:
                        # RN two line: Mode 0,1,2,3
                        temp_sign_data = filled_templates_col[2]

                    rn_width = col_resolution.split("x")[0]
                    temp_sign_data = temp_sign_data.replace("$rw", r"\rw%s" % rn_width)

                sign_data.append(temp_sign_data)

            else:
                sign_data.append(".")

        # print(sign_data)
        return sign_data

    """
    ENTRY POINTS
    """

    def process_isi_data(self, isi_dict, sign_dict):
        """
        This is the new entry point into the module, this replaces the code above as it is no longer valid due to the
        sign data from an eric.bin is no longer used by Hanip.

        I think it should be the main app that decides whether to use remote or local code
        """
        if isi_dict is None:
            pass
        else:
            self.update_isi_dict(isi_dict)

        database_data = self.process_isi_text(sign_dict)

        return database_data

if __name__ == "__main__":
    imh = ISIMessageHandler()




