# Python Plugin SyncDeviceToTimer
#
# Created: 01-nov-2020
# Author: Skarab22
# Collaborator: Syrhus
#
"""
<plugin key="SyncDeviceToTimer" name="SyncDeviceToTimer" author="Skarab22"
        version="1.0.0" externallink="https://github.com/skarab22/Update_timer">
    <params>
	    <param field="Address" label="IP Domoticz" width="250px" default="localhost" required="true"/>
	    <param field="Port" label="Port Domoticz" width="100px" default="8080" required="true"/>
      <param field="Mode5" label="Sync on start" width="75px">
        <options>
          <option label="On" value="1" default="true"/>
          <option label="Off" value="0"/>
        </options>
      </param>
      <param field="Mode6" label="Debug" width="75px">
        <options>
          <option label="False" value="0"/>
          <option label="True" value="2"/>
          <option label="Verbose" value="2+4+8+16+64" default="false"/>
        </options>
      </param>
    </params>
</plugin>
"""

import datetime
from mathparse import mathparse
import requests
# ~ try:
# ~ import DomoticzEx as Domoticz
# ~ except ImportError:
from fakeDomoticz import fakeDomoticz, Parameters, Devices
Domoticz = fakeDomoticz()


now = datetime.datetime.now()

#constants
MON = 1
TUE = 2
WED = 4
THU = 8
FRI = 16
SAT = 32
SUN = 64
EVERYDAYS = 128
WEEKDAYS = 256
WEEKENDS = 512

SUM_EVERYDAYS = 127
SUM_WEEKDAYS = 31
SUM_WEEKENDS = 96

ONTIME = 2

class BasePlugin:
    """sync-timer python plugin for Domoticz"""
    # ~ enabled = False
    __HEARTBEAT: int = 25
    __UNIT: int = 1

    def  __init__(self):
        self.__sync_start = 1 # sync timers on start of plugin, defualt yes
        self.HOST: str
        self.PORT: str
        self.__deviceID: str

    def url(self, json_cmd):
        return 'http://' + self.HOST + ':'+ self.PORT + json_cmd


    def request(self, json_cmd):
        cmd: str = self.url(json_cmd)
        Domoticz.Debug(f"url:{cmd}")
        response: object = requests.get(cmd, timeout=8)
        if response.ok:
            return response.json()
        Domoticz.Error(f"url:{cmd} failed")
        return {}

    def get_timer_list(self,ip, port):
        Domoticz.Debug(f"get_timer_list=> {ip}:{port}")
        self.HOST= ip
        self.PORT= port

        cmd = '/json.htm?type=command&param=getschedules'
        schedules = self.request(cmd)
        timers = {}
        if 'result' in schedules:
            timers = schedules['result']
        Domoticz.Debug(f"timers:{timers}")

        #on filtre les timers actifs
        activeTimers = [timer for timer in timers if timer['Active'] == "true"]

        deviceID: int = 0
        timer_kvp = {}
        timer_list_id = []
        for timer in activeTimers:
            ID = timer['DeviceRowID']
            timer_list_id.append(ID)
            if deviceID != ID:
                timer_kvp[ID] = []
                deviceID = ID

            timer_kvp[ID].append(timer)

        self.active_day(timer_kvp)

    def active_day(self, list_id):
        day = now.weekday()
        for idx,timers in list_id.items():
            Domoticz.Debug(f"entre dans la fonction active_timer avec l'ID = {idx}")
            triggers = sorted(timers, key=byTime)
            Domoticz.Debug(f"Il y a {len(triggers)} timer pour l'idx {idx} ")
            triggers_active_day = []

            for k in enumerate(triggers):
                Domoticz.Debug(f"Days = {k['Days']}")
                if k['Days'] == EVERYDAYS:
                    Domoticz.Debug('Timer actif tous les jours')
                    bin_active_day = SUM_EVERYDAYS
                elif k['Days'] == WEEKDAYS:
                    Domoticz.Debug('Timer actif jours de semaine')
                    bin_active_day = SUM_WEEKDAYS
                elif k['Days'] == WEEKENDS:
                    Domoticz.Debug('Timer actif le weekend')
                    bin_active_day = SUM_WEEKENDS
                else:
                    bin_active_day = int(k['Days'])
                Domoticz.Debug(f"Nous somme le {day}e jour de la semaine. Valeur Days en binaire = {2**day}")
                if bin_active_day & (2**day) :
                    triggers_active_day.append(k)

            if triggers:
                Domoticz.Debug(f"ID des timer actifs pour ce jours de la semaine : {triggers_active_day}")
                self.active_hour(id, triggers_active_day)

    def active_hour(self, device_id, triggers):
        Domoticz.Debug(f"Nb déclencheurs = {len(triggers)}")
        m = 0
        nw = now.time()
        if len(triggers) > 1:
            while not strToTime(triggers[m]) <= nw <= strToTime(triggers[m+1]):
                if m + 2 < len(triggers):
                    m = m + 1
                else:
                    m = m + 1
                    break

        Domoticz.Debug(f" i = {m}, a = {triggers[m]}, now time = {nw.strftime("%H:%M:%S")}, \
                          str_time = {strToTime(triggers[m])}")
        self.update_device(str(device_id), triggers[m])

    def update_device(self, device_id, trigger):
        Domoticz.Debug("update_device:" + device_id)
        cmd = '/json.htm?type=command&param=getdevices&rid=' + device_id
        # ~ result = self.request(cmd)
        if result := self.request(cmd):
            deviceJson =  result['result'][0]
            Domoticz.Debug(f'status {device_id}: {deviceJson} ')

            status = 'On'
            prop = 'Status'
            if 'SwitchType' in deviceJson:
                # ~ selectorType = deviceJson['SwitchType'] == "Selector"
                if selectorType := deviceJson['SwitchType'] == "Selector":
                    prop = 'Level'
                    status = trigger['Level']
                else:
                    status = 'On' if trigger['TimerCmd']==0 else 'Off'

                if status != deviceJson[prop]:
                    cmd = '/json.htm?type=command&param=switchlight&idx=' + device_id + '&switchcmd=' + \
                           ('Set%20Level&level=' if  selectorType else '') + str(status)
                    if self.request(cmd):
                        Domoticz.Log(f'device {device_id} changes to {status} status')
                        # return True
                else:
                    Domoticz.Log(f'device {device_id} already on good state')
            elif 'Type' in deviceJson:
                # ~ selectorType = deviceJson['Type'] == "Setpoint"
                if selectorType := deviceJson['Type'] == "Setpoint":
                    prop = 'SetPoint'
                    # ~ status = trigger['Temperature']
                    if status := trigger['Temperature'] != deviceJson[prop]:
                        cmd = '/json.htm?type=command&param=setsetpoint&idx=' + device_id + '&setpoint=' + str(status)
                        if self.request(cmd):
                            Domoticz.Log(f'device {device_id} changes to {status} status')
                            # return True
                    else:
                        Domoticz.Log(f'device {device_id} already on good setpoint')

    def onStart(self):
        Domoticz.Debugging(parameter2number(Parameters["Mode6"], 2))
        Domoticz.Debug("onStart called")

        self.__deviceID: str = Parameters['Name']
        Domoticz.Heartbeat(self.__HEARTBEAT)
        self.__sync_start = parameter2number(Parameters["Mode5"], 1)

        CreateDeviceEx(self.__deviceID, self.__UNIT)

        if self.__sync_start:
            self.get_timer_list(Parameters["Address"],Parameters["Port"])
        DumpConfigToLog(self.__deviceID)

    def onStop(self):
        Domoticz.Log("Plugin is stopping.")

    def onHeartbeat(self):
        Domoticz.Debug("onHeartbeat")

    def onCommand(self, Unit, Command, Level, Hue):
        # pylint: disable=W0613
        Domoticz.Debug(f"Unit:{Unit}, Command:{Command}")
        self.get_timer_list(Parameters["Address"],Parameters["Port"])
        # pylint: enable=W0613

_plugin = BasePlugin()
def onStart():
    _plugin.onStart()

def onStop():
    _plugin.onStop()

def onCommand(Unit, Command, Level, Hue):
    _plugin.onCommand(Unit, Command, Level, Hue)

def onHeartbeat():
    _plugin.onHeartbeat()

# Generic helper functions
def DumpConfigToLog(deviceId: str):
    showParameters()
    showDevices(deviceId)

def showParameters():
    for x, y in Parameters.items():
        Domoticz.Debug("Parameter '" + x + "'...: '" + str(y) + "'")

def showDevices(deviceId: str):
    for x, y in Devices[deviceId].Units.items():
        Domoticz.Debug("Unit "+str(x)+": " + str(y))
        Domoticz.Debug("Unit Type.........: " + str(y.Type) + " / " + str(y.SubType))

def CreateDeviceEx(DeviceID: str, Unit: int, Type: int=244, Switchtype: int=9, Used: int=1):
    if DeviceID not in Devices or (Unit not in Devices[DeviceID].Units):
        Domoticz.Unit(Name=DeviceID, Unit=Unit, Type=Type, Switchtype=Switchtype, Used=Used,
                      DeviceID=DeviceID).Create()

def byTime(obj):
    val = obj['ScheduleDate'].split(' ')[1]
    return val

def strToTime(trigger):
    val = trigger['ScheduleDate'].split(' ')[1]
    return datetime.time(hour=int(val.split(':')[0]),minute=int(val.split(':')[1]),second=0)

def parameter2number(paramString: str, defaultNumber: float) -> float:
    try:
        return mathparse.parse(paramString)
    except: # pylint: disable=W0702
        return defaultNumber
