# Playhouse: Making buildings into interactive displays using remotely controllable lights.
# Copyright (C) 2014  John Eriksson, Arvid Fahlström Myrman, Jonas Höglund,
#                     Hannes Leskelä, Christian Lidström, Mattias Palo, 
#                     Markus Videll, Tomas Wickman, Emil Öhman.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import tornado.ioloop
import tornado.web

import traceback

import lightgames
import manager


password = None


class GetException(Exception):
    def __init__(self, msg, response):
        super().__init__()
        self.msg = msg
        self.response = response

class ConfigException(Exception):
    def __init__(self, msg):
        super().__init__()
        self.msg = msg

def add_auth_cookie(headers):
    if manager.light_cookie and 'user' in manager.light_cookie:
        headers['Cookie'] = manager.light_cookie['user'].output(attrs=[], header='')
    return headers

def get_data(client, path):
    print("GET %s" % path)
    try: 
        client.request("GET", path, headers=add_auth_cookie({}))
    except ConnectionRefusedError as e: 
        print("ConnectionRefusedError: [Errno 111] Connection refused")
        raise ConfigException(e)

    json = client.getresponse().read().decode()
    print("GET %s response:" % path, json)
    try:
        response = tornado.escape.json_decode(json)
    except ValueError:
        print("ValueError: Did not get json from server when requesting %s" % path)
        print(json)
        raise GetException("<p>Did not get json from server. Is the IP and port correct? Ch
eck the 
output in console</p>", json)
    else:
        if response.get('state',None) == 'success':
            return response
        else:
            raise GetException("<p>Unexpected answer from lamp-server.</p>" +
                "<p>" + str(response) + "</p>" +
                "<p>Expected 'state':'success'</p>", json)

def update_config(cur_cfg, new_cfg, key):
    if key in new_cfg and cur_cfg[key] != new_cfg[key]:
        cur_cfg[key] = new_cfg[key]
        return True
    return False

def use_statusmessage(func):
    def new_func(self, *args, **kwargs):
        tvars = {
            'status':  self.get_argument('status', 'message'),
            'message': self.get_argument('msg', '')
        }
        return func(self, tvars, *args, **kwargs)
    return new_func

class RequestHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        if password is None:
            return "disabled"

        user_json = self.get_secure_cookie("user")
        if user_json:
            return tornado.escape.json_decode(user_json)
        else:
            return None


class ConfigHandler(tornado.web.RequestHandler):
    def get(self):
        self.redirect("/config/setup")


class ConfigLoginHandler(tornado.web.RequestHandler):
    @use_statusmessage
    @tornado.web.removeslash
    def get(self, tvars):
        tvars['next'] = self.get_argument("next", "/config")
        self.render('config_login.html', **tvars)

    def post(self):
        user = self.get_argument("username")
        pwd = self.get_argument("password",None)
        if user == 'admin' and pwd == password:
            self.set_current_user(user)
            self.redirect(self.get_argument("next"))
        else:
            self.set_current_user(None)
            self.redirect("login?next=%s&status=error&msg=%s" % (
                self.get_argument("next"), "Wrong username or password"))

    def set_current_user(self, user):
        if user:
            print("User %s logged in" % user)
            self.set_secure_cookie("user", tornado.escape.json_encode(user),
                expires_days=None)
        else:
            self.clear_cookie("user")


class SetupConfigHandler(RequestHandler): 
    @use_statusmessage
    @tornado.web.removeslash
    @tornado.web.authenticated 
    def get(self, tvars):
        def config(key):
            if key in manager.config:
                return manager.config[key]
            return None

        tvars['config'] = config
        tvars['connection_status'] = manager.client_status
        self.render("config_setup.html", **tvars)

    @tornado.web.authenticated
    def post(self):
        print('POST', self.request.body)

        cfg = {}
        for key in self.request.arguments.keys():
            cfg[key] = self.get_argument(key)

        cfg['lampport'] = int(cfg['lampport'])
        cfg['serverport'] = int(cfg['serverport'])
        cfg['configport'] = int(cfg['configport'])

        status = "message"
        msg = "Setup saved"
        if any([update_config(manager.config, cfg, x) for x in ['lampdest', 'lampport']]):
            manager.client = manager.connect_lampserver()
            try:
                manager.fetch_grid_size()
                manager.client_status = "connected"
                msg = "Connected to lampserver"
            except:
                traceback.print_exc()
                manager.client_status = "error"
                status = "error"
                msg = "Failed to connect to lampserver"

        if update_config(manager.config, cfg, 'serverport'):
            msg = 'Web server port change requires a restart'
        if update_config(manager.config, cfg, 'configport'):
            msg = 'Web server port change requires a restart'

        if manager.config['serverport'] == manager.config['configport']:
            msg = 'Warning: Game server port and config server port are the same'
            status = 'error'


        update_config(manager.config, cfg, 'stream_embedcode')

        if any([update_config(manager.config['idle'], cfg, x)
                for x in ['animation_directory', 'cycle_interval',
                          'transition_time', 'color_off']]):
            msg = 'Idle animation changed, requires reloading the game'

        manager.save_config()

        self.redirect("setup?status=%s&msg=%s" % (status, msg))



class BridgeConfigHandler(RequestHandler):
    bridges = {} 
    @use_statusmessage 
    @tornado.web.removeslash
    @tornado.web.authenticated
    def get(self, tvars):
        if BridgeConfigHandler.bridges == {}: 
            try:
                client = manager.connect_lampserver()
                response = get_data(client, "/bridges")
                BridgeConfigHandler.bridges = response['bridges']
            except ConfigException as e:
                tvars['status']  = "error"
                tvars['message'] = e.msg
            except GetException as e:
                self.write(e.msg)
                return 
        tvars['bridges'] = BridgeConfigHandler.bridges 
        self.render('config_bridges.html', **tvars) 

    @tornado.web.authenticated
    def post(self):
        print('POST', self.request.arguments)
        client = manager.connect_lampserver()
        headers = add_auth_cookie({'Content-Type': 'application/json'})

        data = self.request.arguments 
        if 'identify' in data: 
            if not 'select' in data:
                self.redirect("bridges?status=message&msg=%s" % "You need to select a bridge.")  
            request = {'alert': 'select'}
            request = tornado.escape.json_encode(request) 
            for mac in data['select']: 
                print("Identify POST:", "/bridges/"+mac.decode('utf-8')+"/lights/all", request) 
                client.request("POST", "/bridges/"+mac.decode('utf-8')+"/lights/all", request, headers)

                response = client.getresponse().read().decode() 
                print('Identify response:', respon