#!python
# encoding: utf-8

import os
import json
import asyncio
import time
from transitions import Machine
from rt_hat import PHY as RT_HAT_PHY

CONFIG_FILE = '/usr/share/InnoRoute/INR_PHY_config.json'
TEMP_FILE = '/usr/share/InnoRoute/INR_PHY_config_temp.json'
BLOCKED_FILE = '/usr/share/InnoRoute/blocked.json'
phy_link_status = [1, 1]
phy_link_status_old = [1, 1]
phy_linkspeed = [2, 2]
phy_linkspeed_new = [2, 2]
initial_auto_setup_state = ["init"]

def phy_id2int(phy_id):
    if phy_id == "PHY0":
        return 0
    if phy_id == "PHY2":
        return 1
    return -1

def phy_speed2int(speed):
    if speed == "auto":
        return "auto"
    print(f"Translating speed: {speed}")
    if speed == "10":
        return 0
    if speed == "100":
        return 1
    if speed == "1000":
        return 2

def int2phy_speed(speed):
    if speed == 0:
        return "10"
    if speed == 1:
        return "100"
    if speed == 2:
        return "1000"

class PHYStateMachine:
    states = ['idle', 'checking', 'applying']

    def __init__(self):
        self.machine = Machine(model=self, states=PHYStateMachine.states, initial='idle')
        self.machine.add_transition(trigger='start_check', source='idle', dest='checking')
        self.machine.add_transition(trigger='config_changed', source='checking', dest='applying', before='create_blocked_file')
        self.machine.add_transition(trigger='no_change', source='checking', dest='idle')
        self.machine.add_transition(trigger='apply_done', source='applying', dest='idle', after='remove_blocked_file')

    def create_blocked_file(self):
        try:
            with open(BLOCKED_FILE, 'w') as file:
                json.dump({"status": "blocked"}, file, indent=4)
            print("Blocked file created.")
        except Exception as e:
            print(f"Error creating blocked file: {e}")

    def remove_blocked_file(self):
        try:
            if os.path.exists(BLOCKED_FILE):
                os.remove(BLOCKED_FILE)
                print("Blocked file removed.")
        except Exception as e:
            print(f"Error removing blocked file: {e}")

    async def check_config(self):
        while True:
            print("check_config")
            if self.state == 'idle':
                self.start_check()
                print("Checking configuration...")
                if await self.config_has_changed():
                    self.config_changed()
                    await self.apply_changes()
                    self.apply_done()
                else:
                    self.no_change()  # Transition back to idle if no changes
            await asyncio.sleep(3)

    async def config_has_changed(self):
        print("config_has_changed")
        try:
            with open(CONFIG_FILE, 'r') as file:
                data = json.load(file)
            try:
                with open(TEMP_FILE, 'r') as file:
                    temp_data = json.load(file)
                
                # Get phy link status
                phy_link_status_old=phy_link_status
                (phy_link_status[0], phy_linkspeed[0]) = RT_HAT_PHY.get_link_status(0)
                (phy_link_status[1], phy_linkspeed[1]) = RT_HAT_PHY.get_link_status(1)
                
                # Check: WebUI has changed the config file
                changed_config_webui = False
                if data != temp_data: 
                    with open(TEMP_FILE, 'w') as file:
                        json.dump(data, file, indent=4)
                    changed_config_webui = True

                # Check: User changes interaction with the interfaces and determination of adjusted speed
                changed_config_user = [False, False]
                ii = 0
                phy_linkspeed_json = [0, 0]
                for phy_id in ["PHY0", "PHY2"]:
                    # if not (phy_id in data and f"{phy_id}_state" in data): # debug Check
                    #     print(f"PHY {phy_id} not found in JSON data")
                    phy_linkspeed_json[ii] = phy_speed2int(data[phy_id])
                    if phy_linkspeed_json[ii] != "auto":   
                        if phy_linkspeed_json[ii] != phy_linkspeed[ii]:
                            phy_linkspeed_new[ii] = phy_linkspeed_json[ii]
                            changed_config_user[ii] = True
                        else: 
                            changed_config_user[ii] = False
                    else:
                        print("\n\n",phy_link_status, "\n\n")
             	           if phy_link_status[0] == 1 and phy_link_status[1] == 1:
                            lower_speed = min(phy_linkspeed[0], phy_linkspeed[1])
                            if lower_speed == phy_linkspeed[0] and phy_linkspeed[0]==phy_linkspeed[1]:
                                changed_config_user[ii] = False
                                if initial_auto_setup_state[0] == "adjust":
                                    initial_auto_setup_state[0] = "done"
                            else:
                                changed_config_user[ii] = True
                                phy_linkspeed_new[ii] = lower_speed
                                if initial_auto_setup_state[0] == "done":
                                    initial_auto_setup_state[0] = "init"

                            if initial_auto_setup_state[0] == "init":
                                phy_linkspeed_new[ii] = 3
                                changed_config_user[ii] = True
                        else:
                            if phy_link_status[ii] == 0:
                                data[f"{phy_id}_state"] = "down"
                    ii += 1
                if phy_link_status[0] != phy_link_status_old[0]:
                    changed_config_user[0]=True
                if phy_link_status[1] != phy_link_status_old[1]:
                    changed_config_user[1]=True
                # Return result
                print("webui:"+str(changed_config_webui)+" user0:"+str(changed_config_user[0])+" user1:"+str(changed_config_user[1]))
                if changed_config_webui or changed_config_user[0] or changed_config_user[1]:
                    # Update the json temp file
                    with open(TEMP_FILE, 'w') as file:
                        json.dump(data, file, indent=4)
                    return True
                else:
                    return False
                
            except FileNotFoundError:
                with open(TEMP_FILE, 'w') as file:
                    json.dump(data, file, indent=4)
                return True
            
        except Exception as e:
            print(f"Error reading or processing JSON file: {e}")
            return False

    async def apply_changes(self):
        print("apply_changes")
        try:
            with open(CONFIG_FILE, 'r') as file:
                data = json.load(file)
            ii=0
            for phy_id in ["PHY0","PHY2"]:
                if initial_auto_setup_state[0] == "init":
                    initial_auto_setup_state[0] = "adjust"
                RT_HAT_PHY.phy_force_link_speed(ii, phy_linkspeed_new[ii], 0, 0)
                time.sleep(5)  # Under 3s phy_force_link_speed fails to set the link speed successfully!!!
                RT_HAT_PHY.phy_adv_init(ii, 0, 0)
                ii += 1
            time.sleep(5)
            # Get phy link status
            (phy_link_status[0], phy_linkspeed[0]) = RT_HAT_PHY.get_link_status(0)
            (phy_link_status[1], phy_linkspeed[1]) = RT_HAT_PHY.get_link_status(1)
            if phy_link_status[0]: #make FPGA hardware follow PHY0 if up, else PHY2
                RT_HAT_PHY.set_FPGA(phy_linkspeed[0],0)
                RT_HAT_PHY.set_FPGA(phy_linkspeed[0],1)
            else:
                RT_HAT_PHY.set_FPGA(phy_linkspeed[1],0)
                RT_HAT_PHY.set_FPGA(phy_linkspeed[1],1)

            # update PHY_state in the JSON file
            data[f"PHY0_state"] = str(int2phy_speed(phy_linkspeed[0]))
            data[f"PHY2_state"] = str(int2phy_speed(phy_linkspeed[1]))
            if phy_link_status[0] == 0:
                data[f"PHY0_state"] = "down"
            if phy_link_status[1] == 0:
                data[f"PHY2_state"] = "down"
            # print data to write
            with open(CONFIG_FILE, 'w') as file:
                json.dump(data, file, indent=4)
                
        except Exception as e:
            print(f"Error applying configuration: {e}")

async def main():
    phy_sm = PHYStateMachine()
    await phy_sm.check_config()

if __name__ == "__main__":
    RT_HAT_PHY.init()
    asyncio.run(main())
