#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from mqtt.smarthome import common_base
import config
import geo
import task
import time


def now():
    return time.mktime(time.localtime())


def next_sunrise_time(time_offs_min=30):
    tm = now()
    rv = time.mktime(geo.sun.sunrise(config.GEO_POSITION)) + time_offs_min * 60
    if tm > rv:
        rv = time.mktime(geo.sun.sunrise(config.GEO_POSITION, date=time.localtime(tm + 24 * 60 * 60))) + time_offs_min * 60
    return rv


def next_sunset_time(time_offs_min=-30):
    tm = now()
    rv = time.mktime(geo.sun.sunset(config.GEO_POSITION)) + time_offs_min * 60
    if tm > rv:
        rv = time.mktime(geo.sun.sunset(config.GEO_POSITION, date=time.localtime(tm + 24 * 60 * 60))) + time_offs_min * 60
    return rv


def next_user_time(hh, mm):
    ts = time.localtime()
    tm = time.mktime(ts)
    ut_ts = list(ts)
    ut_ts[3] = hh
    ut_ts[4] = mm
    ut = time.mktime(time.struct_time(list(ts[:3]) + [hh, mm, 0] + list(ts[6:])))
    if ts[3] > hh or (ts[3] == hh and ts[4] >= mm):
        ut += 24 * 60 * 60
    #
    return ut


class day_state(common_base):
    """
    Class to subscribe day events as a callback (see add_callback)

    :param time_start_of_day: Time of a day (tuple including hour and minute) for start of day or None for no start of day state.
    :type time_start_of_day: tuple
    :param time_start_of_night: Time of a day (tuple including hour and minute) for start of night or None for no end of day state.
    :type time_start_of_night:  tuple
    :param time_offset_sunrise: time offset for sunrise in minutes (negative values lead to earlier sunrise state) or None for no sunrise state.
    :type time_start_of_day: int
    :param time_offset_sunset: time offset for sunset in minutes (negative values lead to earlier sunset state) or None for no sunrise state.
    :type time_start_of_day: int
    """
    KEY_SUNRISE = 'sunrise'
    KEY_SUNSET = 'sunset'
    KEY_START_OF_NIGHT = 'start_of_night'
    KEY_START_OF_DAY = 'start_of_day'
    #
    STATES = (KEY_START_OF_DAY, KEY_SUNRISE, KEY_SUNSET, KEY_START_OF_NIGHT)

    def __init__(self, time_start_of_day, time_start_of_night, time_offset_sunrise, time_offset_sunset):
        self.__time_start_of_day__ = time_start_of_day
        self.__time_start_of_night__ = time_start_of_night
        self.__time_offset_sunrise__ = time_offset_sunrise
        self.__time_offset_sunset__ = time_offset_sunset
        super().__init__()
        #

    def get_state(self):
        tm = {}
        if self.__time_offset_sunrise__ is not None:
            tm[next_sunrise_time(self.__time_offset_sunrise__)] = self.KEY_SUNRISE
        if self.__time_start_of_day__ is not None:
            tm[next_user_time(*(self.__time_start_of_day__))] = self.KEY_START_OF_DAY
        if self.__time_offset_sunset__ is not None:
            tm[next_sunset_time(self.__time_offset_sunset__)] = self.KEY_SUNSET
        if self.__time_start_of_night__ is not None:
            tm[next_user_time(*(self.__time_start_of_night__))] = self.KEY_START_OF_NIGHT
        #
        tms = list(tm.keys())
        tms.sort()
        return tm[tms[-1]]


class day_event(day_state):
    """
    Class to subscribe day events as a callback (see add_callback)

    :param time_start_of_day: Time of a day (tuple including hour and minute) for start of day or None for no start of day state.
    :type time_start_of_day: tuple
    :param time_start_of_night: Time of a day (tuple including hour and minute) for start of night or None for no end of day state.
    :type time_start_of_night:  tuple
    :param time_offset_sunrise: time offset for sunrise in seconds (negative values lead to earlier sunrise state) or None for no sunrise state.
    :type time_start_of_day: int
    :param time_offset_sunset: time offset for sunset in seconds (negative values lead to earlier sunset state) or None for no sunrise state.
    :type time_start_of_day: int
    """

    def __init__(self, time_start_of_day=(5, 0), time_start_of_night=(22, 0), time_offset_sunrise=30, time_offset_sunset=-30):
        super().__init__(time_start_of_day, time_start_of_night, time_offset_sunrise, time_offset_sunset)
        #
        current_day_state = self.get_state()
        for key in self.STATES:
            self[key] = current_day_state == key
        #
        cyclic = task.periodic(30, self.__cyclic__)
        cyclic.run()

    def __cyclic__(self, a):
        current_day_state = self.get_state()
        for key in self.STATES:
            self.set(key, current_day_state == key)
