#!/usr/bin/env python3

# Copyright (c) 2014 Erik Johansson <erik@ejohansson.se>
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA

from tellive.tellstick import TellstickLiveClient
from tellive.livemessage import LiveMessage
from tellcore.telldus import TelldusCore, Device, Sensor
import tellcore.constants as const

import argparse
import configparser
import select
import time

# Must match commands in handle_command
SUPPORTED_METHODS = const.TELLSTICK_TURNON \
    | const.TELLSTICK_TURNOFF \
    | const.TELLSTICK_BELL \
    | const.TELLSTICK_DIM \
    | const.TELLSTICK_LEARN \
    | const.TELLSTICK_EXECUTE \
    | const.TELLSTICK_UP \
    | const.TELLSTICK_DOWN \
    | const.TELLSTICK_STOP

def handle_command(device, action, value=None):
    if action == "turnon":
        device.turn_on()
    elif action == "turnoff":
        device.turn_off()
    elif action == "bell":
        device.bell()
    elif action == "dim":
        device.dim(value)
    elif action == "learn":
        device.learn()
    elif action == "execute":
        device.execute()
    elif action == "up":
        device.up()
    elif action == "down":
        device.down()
    elif action == "stop":
        device.stop()
    else:
        print("Unkown command {}", action)

def main(config):
    client = TellstickLiveClient(config['publickey'], config['privatekey'],
                                 debug=config.getboolean('debug'))

    (server, port) = client.connect_to_first_available_server()
    print("Connected to {}:{}".format(server, port))

    core = TelldusCore()

    def on_device_event(device_id, method, data, cid):
        client.report_device_event(device_id, method, data)
    core.register_device_event(on_device_event)

    def on_sensor_event(protocol, model, id, datatype, value, timestamp, cid):
        sensor = Sensor(protocol, model, id, datatype)
        client.report_sensor_values(sensor)
    core.register_sensor_event(on_sensor_event)

    ping_time = time.time()
    pong_time = time.time()

    supported_methods = SUPPORTED_METHODS
    client.register(version=1, uuid=config['uuid'])

    while True:
        try:
            rlist, wlist, xlist = select.select([client.socket], [], [], 0.5)
        except KeyboardInterrupt:
            client.disconnect()
            break

        now = time.time()
        if rlist and rlist[0] == client.socket:
            msg = client.receive_message()

            if msg.subject() == client.SUBJECT_COMMAND:
                params = msg.parameter(0)
                device = Device(params['id'])
                handle_command(device, params['action'], params.get('value'))
                if 'ACK' in params:
                    client.acknowledge(params['ACK'])

            elif msg.subject() == client.SUBJECT_PONG:
                pong_time = now

            elif msg.subject() == client.SUBJECT_REGISTERD:
                methods = msg.parameter(0)['supportedMethods']
                supported_methods = supported_methods & methods
                print("Client is registered, supported methods: "
                      "0x{:x} -> 0x{:x}".format(methods, supported_methods))
                client.report_devices(core.devices(), supported_methods)
                client.report_sensors(core.sensors())

            elif msg.subject() == client.SUBJECT_NOT_REGISTERED:
                print("Activate this client by visiting: '{}'".
                      format(msg.parameter(0)['url']))
                config['uuid'] = msg.parameter(0)['uuid']
                client.disconnect()
                break

            elif msg.subject() == client.SUBJECT_DISCONNECT:
                client.disconnect()
                break

            else:
                print("Unknown subject {}".format(msg.subject()))

        if now - pong_time >= 6 * 60:
            raise TimeoutError("No pong received in 6 minutes")
        if now - ping_time >= 2 * 60:
            ping_time = now
            client.ping()

        core.callback_dispatcher.process_pending_callbacks()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Connect a TellStick to Telldus Live')
    parser.add_argument('config', help='Configuration file to use')
    args = parser.parse_args()

    section = 'settings'
    config = configparser.ConfigParser()
    config[section] = {'publickey': '', 'privatekey': '', 'uuid': '',
                       'debug': False}
    config.read(args.config)

    if not (config[section]['publickey'] and config[section]['privatekey']):
        url = "http://api.telldus.com/keys/index"
        print("Point your browser to '{}' and 'Generate a private token...'".
              format(url))
        config[section]['publickey'] = input("Enter public key: ")
        config[section]['privatekey'] = input("Enter private key: ")

    try:
        main(config[section])
    finally:
        with open(args.config, 'w') as configfile:
            config.write(configfile)
