import logging
import socket
import json
import sys
from queue import Queue
from gi.repository import Gtk, GObject

log = logging.getLogger('Connection')
conn = None
ip = None
port = 9999
command_queue = Queue()
signal_handlers = {}


def establish(host):
    global conn, port, log, ip

    log.info('establishing Connection to %s', host)
    conn = socket.create_connection((host, port))
    log.debug('Connection successful \o/')

    ip = conn.getpeername()[0]
    log.debug('Remote-IP is %s', ip)


def fetchServerConfig():
    global conn, log

    log.info('reading server-config')
    fd = conn.makefile('rw')
    fd.write("get_config\n")
    fd.flush()

    while True:
        line = fd.readline()
        words = line.split(' ')

        signal = words[0]
        args = words[1:]

        if signal != 'server_config':
            continue

        server_config_json = " ".join(args)
        server_config = json.loads(server_config_json)
        return server_config


def enterNonblockingMode():
    global conn, log

    log.debug('entering nonblocking-mode')
    conn.setblocking(False)
    GObject.io_add_watch(conn, GObject.IO_IN, on_data, [''])


def on_data(conn, _, leftovers, *args):
    global log

    '''Asynchronous connection handler. Pushes data from socket
    into command queue linewise'''
    try:
        while True:
            try:
                leftovers.append(conn.recv(4096).decode(errors='replace'))
                if len(leftovers[-1]) == 0:
                    log.info("Socket was closed")

                    # FIXME try to reconnect
                    conn.close()
                    Gtk.main_quit()
                    return False

            except UnicodeDecodeError as e:
                continue
    except:
        pass

    data = "".join(leftovers)
    del leftovers[:]

    lines = data.split('\n')
    for line in lines[:-1]:
        log.debug("got line: %r", line)

        line = line.strip()
        log.debug('re-starting on_loop scheduling')
        GObject.idle_add(on_loop)

        command_queue.put((line, conn))

    if lines[-1] != '':
        log.debug("remaining %r", lines[-1])

    leftovers.append(lines[-1])
    return True


def on_loop():
    '''Command handler. Processes commands in the command queue whenever
    nothing else is happening (registered as GObject idle callback)'''

    global command_queue

    log.debug('on_loop called')

    if command_queue.empty():
        log.debug('command_queue is empty again, stopping on_loop scheduling')
        return False

    line, requestor = command_queue.get()

    words = line.split()
    if len(words) < 1:
        log.debug('command_queue is empty again, stopping on_loop scheduling')
        return True

    signal = words[0]
    args = words[1:]

    log.info('received signal %s, dispatching', signal)
    if signal not in signal_handlers:
        return True

    for handler in signal_handlers[signal]:
        cb = handler['cb']
        if 'one' in handler and handler['one']:
            log.debug('removing one-time handler')
            del signal_handlers[signal]

        cb(*args)

    return True


def send(command, *args):
    global conn, log
    if len(args) > 0:
        command += ' ' + (' '.join(args))

    command += '\n'

    conn.send(command.encode('ascii'))


def on(signal, cb):
    if signal not in signal_handlers:
        signal_handlers[signal] = []

    signal_handlers[signal].append({'cb': cb})


def one(signal, cb):
    if signal not in signal_handlers:
        signal_handlers[signal] = []

    signal_handlers[signal].append({'cb': cb, 'one': True})