#!/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(
        "--no-reasoner", action="store_false", help="do not use the reasoner"
    )
    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, enable_reasoner=args.no_reasoner)

    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")
