
# Slixmpp: The Slick XMPP Library
# Implementation of xeps for Internet of Things
# http://wiki.xmpp.org/web/Tech_pages/IoT_systems
# Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se
# This file is part of Slixmpp.
# See the file LICENSE for copying permission.

class Device(object):
    """
    Example implementation of a device control object.

    The device object may by any custom implementation to support
    specific devices, but it must implement the functions:
          has_control_field
          set_control_fields
    """

    def __init__(self, nodeId):
        self.nodeId = nodeId
        self.control_fields = {}

    def has_control_field(self, field, typename):
        """
        Returns true if the supplied field name exists
        and the type matches for control in this device.

        Arguments:
            field      -- The field name
            typename   -- The expected type
        """
        if field in self.control_fields and self.control_fields[field]["type"] == typename:
            return True
        return False

    def set_control_fields(self, fields, session, callback):
        """
        Starts a control setting procedure. Verifies the fields,
        sets the data and (if needed) and calls the callback.

        Arguments:
            fields   -- List of control fields in tuple format:
                        (name, typename, value)
            session  -- Session id, only used in the callback as identifier
            callback -- Callback function to call when control set is complete.

                    The callback function must support the following arguments:

                session     -- Session id, as supplied in the
                               request_fields call
                nodeId      -- Identifier for this device
                result      -- The current result status of the readout.
                               Valid values are:
                               "error"  - Set fields failed.
                               "ok"     - All fields were set.
                error_field -- [optional] Only applies when result == "error"
                               The field name that failed
                               (usually means it is missing)
                error_msg   -- [optional] Only applies when result == "error".
                               Error details when a request failed.
        """

        if len(fields) > 0:
            # Check availability
            for name, typename, value in fields:
                if not self.has_control_field(name, typename):
                    self._send_control_reject(session, name, "NotFound", callback)
                    return False

        for name, typename, value in fields:
            self._set_field_value(name, value)

        callback(session, result="ok", nodeId=self.nodeId)
        return True

    def _send_control_reject(self, session, field, message, callback):
        """
        Sends a reject to the caller

        Arguments:
            session  -- Session id, see definition in
                        set_control_fields function
            callback -- Callback function, see definition in
                        set_control_fields function
        """
        callback(session, result="error", nodeId=self.nodeId, error_field=field, error_msg=message)

    def _add_control_field(self, name, typename, value):
        """
        Adds a control field to the device

        Arguments:
            name     -- Name of the field
            typename -- Type of the field, one of:
                        (boolean, color, string, date, dateTime,
                         double, duration, int, long, time)
            value    -- Field value
        """
        self.control_fields[name] = {"type": typename, "value": value}

    def _set_field_value(self, name, value):
        """
        Set the value of a control field

        Arguments:
            name     -- Name of the field
            value    -- New value for the field
        """
        if name in self.control_fields:
            self.control_fields[name]["value"] = value

    def _get_field_value(self, name):
        """
        Get the value of a control field. Only used for unit testing.

        Arguments:
            name     -- Name of the field
        """
        if name in self.control_fields:
            return self.control_fields[name]["value"]
        return None
