# *****************************************************************************
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Module authors:
#   Anik Stark <anik.stark@psi.ch>
# *****************************************************************************
"""used for EPC8210 power line switch"""

import re
import struct
from frappy.core import BytesIO, HasIO, Writable, Parameter, Property, IntRange, \
    BoolType, BLOBType, Command


class IO(BytesIO):
    default_settings = {'baudrate': 115200}
    # pattern matches the terminal-screen-like answer, to include '\n' in .* use flag re.DOTALL
    PAT = re.compile(b''.join([f'.*<{i}> (ON|OFF)'.encode() for i in range(1,9)]), re.DOTALL)
    switch_status = None

    @Command((BLOBType(), IntRange(0)), result=BLOBType(0,999))
    def communicate(self, request, replylen):
        return super().communicate(request, replylen)

    def initModule(self):
        self.modules = []
        super().initModule()

    def register(self, module):
        self.modules.append(module)

    def doPoll(self):
        # read values
        reply = self.communicate(b'\x1b\r', 360)    # binary escape, take first 360 bytes of answer
        match = self.PAT.match(reply)
        if match:
            self.switch_status = match.groups()
            self.log.debug('%r', self.switch_status)
        else:
            # avoid recursive issue if no match is made
            self.switch_status = ['OFF'] * 8
            return
        for module in self.modules:
            module.update_values()


class Base(HasIO):

    def initModule(self):
        self.io.register(self)
        super().initModule()

    def get_value(self, addr):
        return self.io.switch_status[addr - 1] == b'ON'

    def update_values(self):
        self.read_value()


class Switch(Base, Writable):

    ioClass = IO

    value = Parameter('state of power line (on/off)', datatype=BoolType())
    target = Parameter('target value of power line (on/off)', datatype=BoolType(), readonly=False)
    addr = Property('address of switch', datatype=IntRange(1, 8))

    def read_value(self):
        if self.io.switch_status is None:
            self.io.doPoll()
        return self.get_value(self.addr)

    def write_target(self, target):
        cmd = 0x32 - int(target)
        command = struct.pack('BBBBBB', 0x80, cmd, self.addr, cmd ^ self.addr, 0x1b, 0xd)
        reply = self.communicate(command, 360)
        match = self.io.PAT.match(reply)
        if match:
            self.io.switch_status = match.groups()
        return self.read_value()
