#!/usr/bin/env python3


"""
This file is part of the Tetris project.

Written 2023 by Maarten Tromp <maarten@geekabit.nl>


Website
-------
https://www.geekabit.nl/projects/tetris-window/


License
-------
CC0 (no copyright, public domain)
The person who associated a work with this deed has dedicated the work to the public domain by
waiving all of his or her rights to the work worldwide under copyright law, including all related
and neighbouring rights, to the extent allowed by law. You can copy, modify, distribute and perform
the work, even for commercial purposes, all without asking permission.
https://creativecommons.org/share-your-work/public-domain/cc0/
"""


# imports
import sys
import signal
import logging
import json
import asyncio
import websockets


# project imports
import config
from led import Led
#from sens import Sens


# globals
logging.basicConfig(format = "%(asctime)s %(levelname)s %(message)s", level = logging.INFO)
LED = Led(config.WIDTH, config.HEIGHT)


def init():
	"""
	Initialize start main loop.

	Parameters
	----------
	none

	Returns
	-------
	none
		This function will never return since the main loop never finishes.
	"""

	logging.info("display init")
	signal.signal(signal.SIGINT, handle_sigint)

	logging.info("starting main loop")
	asyncio.run(main())


async def main():
	"""
	Main loop. Receive messages via websocket.

	Parameters
	----------
	none

	Returns
	-------
	none
		This function will never return since it's an endless loop.
	"""

	logging.info("backend url: %s", config.URL)

	while True:
		try:
			logging.info("connecting...")
			async with websockets.connect(config.URL) as websocket:
				logging.info("connected")
				while True:
					message_raw = await websocket.recv()
					handle_message(message_raw)
			logging.info("disconnected")
		except websockets.exceptions.ConnectionClosedError as exception:
			logging.warning("connection error: %s", repr(exception))


def handle_message(message_raw):
	"""
	Handle incoming messages. Send to associated handlers.

	Parameters
	----------
	message_raw: string
		Json encoded message.

	Returns
	-------
	bool
		True on success, False otherwise.
	"""

	logging.debug("message_raw: %s", message_raw)

	try:
		# decode json
		message_obj = json.loads(message_raw)
	except json.decoder.JSONDecodeError:
		logging.warning("invalid json: %s", message_raw)
		return False

	# send to appropriate handler
	if "screen" in message_obj:
		handle_message_screen(message_obj["screen"])
	else:
		logging.warning("unknown message: %s", message_obj)
		return False
	return True


def handle_message_screen(message):
	"""
	Handle incoming display messages.

	Parameters
	----------
	message: object
		Decoded message as object.

	Returns
	-------
	none
	"""

	logging.debug("message: %s", message)
	LED.write(message)


def handle_sigint(sig, frame):
	"""
	Clear screen and exit on CTRL-C / SIGINT.

	Parameters
	----------
	none

	Returns
	-------
	none
	"""

	logging.info("recieved SIGINT")
	del sig, frame

	logging.info("clearing screen")
	LED.clear()

	logging.info("quitting")
	sys.exit(0)


if __name__ == "__main__":
	init()
