"""
iDigi Professional development kit presentation.

It will subscribe the XBib USB and Serial buttons channels to detect 
when they have been pressed, and, depending on the button pressed it 
will perform the corresponding operation:

XBib USB:
    Button 1: Toggles XBib Serial Led 1
    Button 2: Toggles XBib Serial Led 2
    Button 3: Toggles XBib Serial Led 3
    Button 4: Toggles all XBib Serial Leds

XBib Serial:
    Button 1: Toggles XBib USB Led 1
    Button 2: Toggles XBib USB Led 2
    Button 3: Toggles XBib USB Led 3
    Button 4: Toggles all XBib USB Led


Configuration settings:

xbib_serial_board = Name of the XBib Serial board element
xbib_usb_board = Name of the XBib USB board element

Presentation declaration example:

- name: xbib_presentation0
    driver: custom_presentations.dvkit_presentation:DvkitPresentation
    settings:
        xbib_serial_board: xbib0
        xbib_usb_board: xbib1
"""

# Imports
from settings.settings_base import SettingsBase, Setting
from presentations.presentation_base import PresentationBase
from samples.sample import Sample
import time

# Main class
class DvkitPresentation(PresentationBase):
    # Variables
    __xbib_name = ''
    __channels = {}
    __led_values = {}

    def __init__(self, name, core_services):
        self.__name = name
        self.__core = core_services

        # Declare the settings list for the presentation
        settings_list = [
            Setting(name="xbib_serial_board", type=str, required=True), 
            Setting(name="xbib_usb_board", type=str, required=True)]

        PresentationBase.__init__(self, name=name, 
                                  settings_list=settings_list)

    def apply_settings(self):
        """
            Apply settings as they are defined by the configuration file.
        """

        SettingsBase.merge_settings(self)
        accepted, rejected, not_found = SettingsBase.verify_settings(self)
        if len(rejected) or len(not_found):
            print "Settings rejected/not found: %s %s" % (rejected, not_found)

        SettingsBase.commit_settings(self, accepted)

        return (accepted, rejected, not_found)
    
    def start(self):
        """
            Starts the presentation object.
        """

        self.cm = self.__core.get_service("channel_manager")
        self.cp = self.cm.channel_publisher_get()
        self.cdb = self.cm.channel_database_get()

        # Get xbib and wallrouter names
        self.__xbib_usb_name = SettingsBase.get_setting(self, 
                                                        'xbib_usb_board')
        self.__xbib_serial_name = SettingsBase.get_setting(self, 
                                                           'xbib_serial_board')

        # Declare the channels we are going to work with
        # XBIB USB Board
        self.usb_chan_led1 = self.cdb.channel_get(
                        '.'.join((self.__xbib_usb_name,"led1")))
        self.usb_chan_led2 = self.cdb.channel_get(
                        '.'.join((self.__xbib_usb_name,"led2")))
        self.usb_chan_led3 = self.cdb.channel_get(
                        '.'.join((self.__xbib_usb_name,"led3")))
        # XBIB Serial Board
        self.serial_chan_led1 = self.cdb.channel_get(
                        '.'.join((self.__xbib_serial_name,"led1")))
        self.serial_chan_led2 = self.cdb.channel_get(
                        '.'.join((self.__xbib_serial_name,"led2")))
        self.serial_chan_led3 = self.cdb.channel_get(
                        '.'.join((self.__xbib_serial_name,"led3")))

        # Generate the button names
        # XBIB USB Board
        usb_b1_name = '.'.join((self.__xbib_usb_name,"sw1"))
        usb_b2_name = '.'.join((self.__xbib_usb_name,"sw2"))
        usb_b3_name = '.'.join((self.__xbib_usb_name,"sw3"))
        usb_b4_name = '.'.join((self.__xbib_usb_name,"sw4"))
        # XBIB Serial Board
        serial_b1_name = '.'.join((self.__xbib_serial_name,"sw1"))
        serial_b2_name = '.'.join((self.__xbib_serial_name,"sw2"))
        serial_b3_name = '.'.join((self.__xbib_serial_name,"sw3"))
        serial_b4_name = '.'.join((self.__xbib_serial_name,"sw4"))

        # Create a dictionary containing the leds channels and the channel 
        # name of the associated button as ID
        self.__channels[usb_b1_name] = (self.serial_chan_led1,)
        self.__channels[usb_b2_name] = (self.serial_chan_led2,)
        self.__channels[usb_b3_name] = (self.serial_chan_led3,)
        self.__channels[serial_b1_name] = (self.usb_chan_led1,)
        self.__channels[serial_b2_name] = (self.usb_chan_led2,)
        self.__channels[serial_b3_name] = (self.usb_chan_led3,)
        self.__channels[usb_b4_name] = (self.serial_chan_led1, 
                                        self.serial_chan_led2, 
                                        self.serial_chan_led3)
        self.__channels[serial_b4_name] = (self.usb_chan_led1, 
                                           self.usb_chan_led2, 
                                           self.usb_chan_led3)

        # Initialize the led_values dictionary
        self.__led_values = {self.usb_chan_led1:False, 
                             self.usb_chan_led2:False, 
                             self.usb_chan_led3:False, 
                             self.serial_chan_led1:False, 
                             self.serial_chan_led2:False, 
                             self.serial_chan_led3:False}

        # Update the value of each led channel
        self.update_led_values()

        # Subscribe to the XBIB boards button channels. Execute the 
        # 'button_status_changed' method as callback
        self.cp.subscribe(usb_b1_name, self.button_status_changed)
        self.cp.subscribe(usb_b2_name, self.button_status_changed)
        self.cp.subscribe(usb_b3_name, self.button_status_changed)
        self.cp.subscribe(usb_b4_name, self.button_status_changed)
        self.cp.subscribe(serial_b1_name, self.button_status_changed)
        self.cp.subscribe(serial_b2_name, self.button_status_changed)
        self.cp.subscribe(serial_b3_name, self.button_status_changed)
        self.cp.subscribe(serial_b4_name, self.button_status_changed)

    def stop(self):
        """
            Stop the presentation object.
        """

        return True

    def button_status_changed(self, channel):
        """
            Perform the corresponding tasks depending on the button that 
            has been pressed.
        """

        # Get the name of the channel which has changed its status
        ch = channel.name()
        # Obtain the value of the button channel
        value = channel.get().value

        # Perform the corresponding action only if the button has been 
        # pressed (avoiding release events)
        if not value:
            # Refresh the value of all the leds, they could have been 
            # changed externally
            self.update_led_values()
            # Toggle the LED/s corresponding to the pressed button
            for led in self.__channels[ch]:
                led.set(Sample(time.time(),not self.__led_values[led]))

    def update_led_values(self):
        """
            Save in the led_values dictionary the value of each led channel.
        """

        for led_channel in self.__led_values:
            self.__led_values[led_channel] = led_channel.get().value

