#!/usr/bin/env python
# -*- coding: utf-8 -*-

from minimalkb import __version__

import logging; logger = logging.getLogger("minimalKB");


import sys
import asyncore, asynchat
import os, socket, string
import traceback

import json


PORT = 6969

class MinimalKBChannel(asynchat.async_chat):

    def __init__(self, server, sock, addr, kb):
        asynchat.async_chat.__init__(self, sock)
        self.set_terminator("#end#".encode("utf-8"))
        self.request = None
        self.data = ""

        self.kb = kb

    def parse_request(self, req):
        tokens = [a.strip() for a in req.strip().split("\n")]
        if len(tokens) == 1:
            return tokens[0], [], {}
        else:
            args = []
            for t in tokens[1:]:
                try:
                    args.append(json.loads(t))
                except ValueError:
                    logger.error("Invalid arguments for <%s>: %s. Valid JSON was expected."%(tokens[0], t))

            if isinstance(args[-1], dict) and "kwargs" in args[-1]:
                kwargs = args[-1]["kwargs"]
                args = args[:-1]
            else:
                kwargs = {}
            return tokens[0], args, kwargs

    def collect_incoming_data(self, data):
        self.data = self.data + data.decode()

    def found_terminator(self):

        logger.debug("Got request:" + self.data )
        request, args, kwargs = self.parse_request(self.data)
        self.data = ""
        self.kb.submitrequest(self, request, *args, **kwargs)

        if request == "close":
            self.close_when_done()

    def sendmsg(self, msg):
        status, res = msg

        raw = ""
        if status == "ok":
            raw = "ok\n%s\n" % json.dumps(res, ensure_ascii=False) if res is not None else "ok\n"
        elif status == "error":
            raw = "error\nkberror\n%s\n" % str(res)
        elif status == "event":
            raw = "event\n%s\n%s\n" % (res.id, json.dumps(res.content, ensure_ascii=False))
        else:
            raise RuntimeError("Unexpected message status: %s" % status)

        logger.debug("Sent message:" + raw)
        self.push((raw + "#end#\n").encode('utf-8'))


class MinimalKBServer(asyncore.dispatcher):

    def __init__(self, port, kb):
        asyncore.dispatcher.__init__(self)

        self.kb = kb

        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(("", port))
        self.listen(5)

    def handle_accept(self):
        conn, addr = self.accept()
        MinimalKBChannel(self, conn, addr, kb)

def version():
    print("minimalKB %s" % __version__)

if __name__ == '__main__':

    from minimalkb.ansistrm import ColorizingStreamHandler
    import argparse

    parser = argparse.ArgumentParser(description='A minimal knowledge base for robots.')
    parser.add_argument('-v', '--version', action='version',
                       version=__version__, help='returns minimalKB version')
    parser.add_argument('-q', '--quiet', action='store_true',
                                help='be quiet (only errors are reported)')
    parser.add_argument('-d', '--debug', action='store_true',
                                help='enables verbose output')
    parser.add_argument('-p', '--port', default=PORT, type=int, nargs='?',
                                help='port the server listen to.')
    parser.add_argument('ontology', nargs='*', help="Files (OWL, RDF or raw triples) with initial ontologies to load")

    args = parser.parse_args()

    console = ColorizingStreamHandler()
    
    if args.debug:
        logger.setLevel(logging.DEBUG)
    elif args.quiet:
        logger.setLevel(logging.ERROR)
    else:
        logger.setLevel(logging.INFO)

    formatter = logging.Formatter('%(asctime)-15s: %(message)s')
    console.setFormatter(formatter)
    logger.addHandler(console)


    # The logging handlers are now setup, import the other minimalkb components
    from minimalkb.kb import MinimalKB, KbServerError

    kb = MinimalKB(args.ontology)

    s = MinimalKBServer(args.port, kb)
    logger.info("Starting to serve at port %d..." % args.port)

    try:
        while True:
            asyncore.loop(count = 1)
            kb.process()
    except KeyboardInterrupt:
        kb.stop_services()
        logger.info("Bye bye")
