# -*- coding: utf-8 -*-
__author__ = 'ke4roh'
# Specifics of communication channels with the Si4707
#
# Copyright © 2016 James E. Scarborough
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

import RPi.GPIO as gpio
from time import sleep
import signal
import Adafruit_GPIO.I2C
from RPiNWR.Si4707 import Context

# TODO wrap this so that it will be cleaned up EVERY time
# --- see http://stackoverflow.com/questions/865115/how-do-i-correctly-clean-up-a-python-object#865272


class AIWIBoardContext(Context):
    """
    This class encapsulates the context through which the radio is used,
    providing adapters to the functionality to reset the radio,
    write to it, and read from it.  A duck-wise compatible class can be used
    to adapt to any context.

    This class is not thread-safe.

    This must be instantiated from the main thread - which is the default, because it sets
    interrupt handlers.  If you need to set your own interrupt handlers, ensure that you call
    shutdown to clean things up, else you won't be able to run it again without power-cycling
    your Raspberry Pi.

    """
    __signals_trapped = False

    relay_gpio_pins = [13, 19]

    i2c = Adafruit_GPIO.I2C.get_i2c_device(0x11)

    def __init__(self):
        super(AIWIBoardContext, self).__init__()
        self.gpio_started = False

    def reset_radio(self):
        """
        Reset the Si4707 chip
        """
        # Ref https://github.com/AIWIndustries/Pi_4707/blob/master/firmware/NWRSAME_v2.py
        if self.gpio_started:
            gpio.cleanup()
        self.gpio_started = True
        gpio.setmode(gpio.BCM)  # Use board pin numbering

        gpio.setup(17, gpio.OUT)  # Setup the reset pin
        gpio.output(17, gpio.LOW)  # Reset the Si4707.
        sleep(0.4)
        gpio.output(17, gpio.HIGH)

        gpio.setup(23, gpio.IN, pull_up_down=gpio.PUD_UP)
        gpio.add_event_detect(23, gpio.FALLING)

        # Initialize the onboard relays
        for pin in self.relay_gpio_pins:
            gpio.setup(pin, gpio.OUT)  # setup gpio pin for relay
            gpio.output(pin, gpio.LOW)  # boot to disabled state

        # set up the LED
        # https://www.reddit.com/r/raspberry_pi/comments/3641ug/blinking_an_onboard_led_on_the_pi_2_model_b/
        # http://raspberrypi.stackexchange.com/questions/697/how-do-i-control-the-system-leds-using-my-
        # sudo echo none >/sys/class/leds/led0/trigger
        # GPIO 16 LOW is on, HIGH is off
        gpio.setup(16, gpio.OUT)
        gpio.output(16, gpio.HIGH)

        sleep(1.5)

    def write_bytes(self, data):
        # TODO make this accept bytes(...)
        if len(data) == 1:
            self.i2c.write8(data[0], 0)
        else:
            self.i2c.writeList(data[0], data[1:])

    def read_bytes(self, num_bytes):
        # TODO make this return bytes(...)
        return self.i2c.readList(0, num_bytes)

    def __enter__(self):
        # Make sure to cleanup GPIO afterward
        if not self.__signals_trapped:
            self.__signals_trapped = True
            for sig in [signal.SIGQUIT, signal.SIGTERM, signal.SIGTSTP]:
                if hasattr(signal.getsignal(sig), '__call__'):
                    deleg = signal.getsignal(sig)

                    def delegate(signum, stack):
                        self.__exit__(None, None, None)
                        deleg(signum, stack)

                    signal.signal(sig, delegate)
                else:
                    def delegate(signum, stack):
                        self.__exit__(None, None, None)

                    signal.signal(sig, delegate)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if self.gpio_started:
                gpio.cleanup()
                self.gpio_started = False
        except RuntimeError:
            self._logger.info("Cleanup trouble", exc_info=True)
            pass  # Probably tried to do it twice

    def relay(self, num, on):
        if on:
            status = gpio.HIGH
        else:
            status = gpio.LOW
        gpio.output(self.relay_gpio_pins[num], status)

    def led(self, on):
        if not on:
            status = gpio.HIGH
        else:
            status = gpio.LOW
        gpio.output(16, status)