#!/usr/bin/python3
import logging
import socket
import json
import sys
from queue import Queue
from gi.repository import Gtk, GObject

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

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

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

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()
		if command_queue.empty():
			log.debug('command_queue was empty, 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 False

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