'''
Devices

Created on 23 août 2022

@author: denis@e-educ.fr

    Flow and Frequency output of the YF-S201

    Flow(L/H)        Frequency(Hz)
    120                16
    240                32.5
    360                49.3
    480                65.5
    600                82
    720                90.2

    F = 7.5 Q     Q: l/mn
'''
import logging, threading, random
from modules import sensors as dev, utils, gpiolib as gpio


logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


class YF_S201(dev.InputDevice):
    waterflow_timeout = 2
    pulse_counter = 0
    start_counter = False
    stop_counter = None

    def __init__(self, pin, register=None, publish=None):
        super().__init__(pin, register=register, callback=self.on_count_pulse, pull_up_down=gpio.IO_PULL_UP,)
        self.publish = publish
        threading.Thread(target=self.waterflow_process, args=()).start()

    def waterflow_publish(self, flow=None):
        if self.publish is not None:
            self.publish(dict(
                    uuid=self.key,
                    topic="waterflow",
                    ts=utils.ts_now(),
                    flow=flow,
                )
            )

    def on_count_pulse(self, channel):
        if self.start_counter:
            self.pulse_counter += 1

    def wait_for_counts(self, timeout):
        self.pulse_counter = 0
        self.start_counter = True
        threading.Event().wait(timeout)
        self.start_counter = False

    def waterflow_process(self):
        self.stop_counter = threading.Event()
        while not self.stop_counter.is_set():
            self.wait_for_counts(self.waterflow_timeout)
            flow = self.pulse_counter / 7.5
            if flow:
                logger.info(f'\nwaterflow_process flow = {flow}')
                self.waterflow_publish(flow)

    def stop(self):
        self.stop_counter.set()


class Relay(dev.RelayFromShiftOut):

    def __init__(self, shift_out_register, register=None, publish=None):
        super().__init__(shift_out_register, register, relay_timeout=1.0)
        self.publish = publish
        self.start_time, self.end_time = 0, 0
        self.started = False
        self.duration_timer = threading.Event()
        self.water_flow_timer = threading.Event()

        self.flow, self.rate = 0.00,  0.00
        self.flow_meter_start()
        self.relay_publish('init', index=self.index, label=self.label, dev_type=self.dev_type)

    def now(self):
        return utils.ts_now_s()

    def relay_publish(self, topic, **kwargs):
        if self.publish is not None:
            self.publish(topic, **dict(
                    uuid=self.key,
                    ts=utils.ts_now_s(),
                    relay_state=self.is_on(),
                    elapsed=self.time_elapsed(),
                    remaining=self.time_remaining(),
                    flow=self.flow,
                    rate=self.rate,
                    **kwargs
                )
            )

    def time_elapsed(self):
        elapsed = self.now() - self.start_time
        if self.start_time <= 0 or elapsed <= 0:
            elapsed = 0
        return elapsed

    def time_remaining(self):
        remaining = self.end_time - self.now()
        if remaining < 0:
            remaining = 0
        return remaining

    def is_it_over(self, timeout):
        elapsed =  self.now() - self.start_time
        if elapsed > timeout:
            self.duration_timer.set()

    def _open(self, timeout):
        self.duration_timer = threading.Event()
        self.started = True
        while not self.duration_timer.is_set():
            try:
                if self.started:
                    self.is_it_over(timeout)
                threading.Event().wait(1.0)
            except Exception as e:
                logger.error(f'run error {e}')
        self.start_time = 0
        self.relay_stop()

    def relay_start(self, duration=0):
        self.start_time = self.now()
        self.end_time = self.start_time + duration
        self.open()
        if duration > 0:
            threading.Thread(target=self._open, args=(duration, )).start()
        self.rate, self.flow = 0.00, 0.00
        self.begin = self.now()
        self.water_flow_timer.set()
        self.relay_publish('start')

    def relay_stop(self):
        self.duration_timer.set()
        self.close()
        self.started = False
        self.water_flow_timer.clear()
        self.relay_publish('stop')

    def relay_halt(self):
        if self.is_on():
            self.started = False

    def relay_restart(self):
        if self.is_on():
            self.started = True

    def relay_status(self):
        self.relay_publish('status')

    def _flow_meter(self):
        while True:
            try:
                self.water_flow_timer.wait()
                elapsed = (self.now() - self.begin)/60
                self.flow = random.randint(1050, 1065)/100 * elapsed
                self.rate = self.flow / elapsed if elapsed > 0 else 0.00
                threading.Event().wait(1.0)
            except Exception as e:
                logger.error(f'flow_meter_process error {e}')
                threading.Event().wait(1.0)

    def flow_meter_start(self):
        self.begin = self.now()
        threading.Thread(target=self._flow_meter).start()




