#! /usr/bin/env python3.5
# Copyright 2017 Digital
#
# This file is part of BeeWatch.
#
# BeeWatch 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.
#
# BeeWatch 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 BeeWatch.  If not, see <http://www.gnu.org/licenses/>.

import curio
import blinker
import logging
import logging.handlers
import digilib.network
import os
import queue
import select
import socket
import sys
import threading
import time
import traceback

import digilib.pin
import digilib.network
import beewatch
# import beewatch.server
import beewatch.pinapi

log = logging.getLogger(__name__+"")
# lapi = logging.getLogger(__name__+".api")
# lschat = logging.getLogger(__name__+".schat")
lch = logging.getLogger(__name__+".chandler")
lserver = logging.getLogger(__name__+".server")


class ConnHandlerBeeWatch(digilib.network.ConnHandler):
    """
    ConnHandlerBeeWatch is the connection handler for the BeeWatch server.

    It parses commands to api calls.

    Todo
    ----
        Implement a permission system
    """
    def __init__(self, socket, addr, server):
        super(ConnHandlerBeeWatch, self).__init__(socket, addr, server)

    async def handle(self, data):
        """
        The handle method parses commands and responds if a command was not
        found. It executes synchronous and asynchronous methods correctly and
        logs the traceback if an exception occured.
        """
        data = data.strip()
        data = data.split(" ")
        cmd,*args = data
        func = beewatch._commands.get(cmd,False)
        if not func:
            await self.respond("Unknown command")
            return
        kwargs = {"args":args,"command":cmd,"respond":self.respond}
        task = None
        try:
            coro = func(**kwargs)
            if hasattr(coro,"__await__"):
                task = await coro
        except Exception as e:
            lch.error("api_func raised an error:",exc_info=e)
            tb = traceback.format_exc()
            await self.respond(tb,log_msg="traceback of '{}'"
                .format(e.__cause__))
        finally:
            pass
        if task:
            lch.debug("exec: "+task.exception.__cause__)
        # task joins iself to suppress the "task not joined" warning
        cur_task = await curio.current_task()
        await curio.ignore_after(0,cur_task.wait)

    async def respond(self,text,*args,log_msg=False):
        """
        this method is passed to apis so they can give feedback to the user
        """
        await self.send(text,log_msg)


class BeeWatchServer(digilib.network.Server):
    """
    BeeWatchServer opens a secured connection to the outside world (or a file socket).

    BeeWatchServer inherits from digilib.network.Server. It connects to the gpio control socket and adds protetction by requireing user authentification.

    Warning
    -------
        as these features aren't implemented yet, the connection is *not* secure at all
    Todo
    ----
        actually implement these features, right now BeeWatchServer does only debug stuff

    Parameters are identical to `digilib.network.Server` if not specified in the following list:

    Parameters
    ----------
        *args:
            passed to :obj:`digilib.network.Server`
        handler_class:
            Same as in `digilib.network.Server` but defaults to :obj:`ConnHandlerBeeWatch`.
        **kwargs:
            passed to :obj:`digilib.network.Server`
    """
    def __init__(
            self,
            *args,
            handler_class=ConnHandlerBeeWatch,
            **kwargs
        ):
        super(BeeWatchServer, self).__init__(
            *args,
            handler_class=handler_class,
            **kwargs,
        )

    def make_socket(self) -> socket.socket :
        s = super(BeeWatchServer,self).make_socket()
        # only for debugging because I'm impatient and don't want to wait
        lch.warning("setting sokopt SO_REUSEADDR to 1. DEBUGGING ONLY")
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s


































#
