/** 
 * \file keyboard.c
 * \brief keyboard source file
 * \details File contain functions definitions for 4x4 keyboard
 *
 * \author Juraj Golej
 * \version 1.0
 */


#include "keyboard.h"

static uint8_t gRow[KB_ROW_COUNT];
static struct PIO_irq gCol[KB_COL_COUNT];
keyboard_cb gCallbackFunc = NULL;

static const uint8_t gMatrix[KB_ROW_COUNT][KB_COL_COUNT] = KB_CODES;

static void _KEYBOARD_irq(struct PIO_irq* const module)
{
	static uint8_t pressCol = 0;
	static uint8_t pressRow = 0;
	static struct PIO_irq* clicked = NULL;

	uint8_t columnPin = PIO(module->port, module->pin);

	if(PIO_pin_level(columnPin)) {
		if(module == clicked) {
			gCallbackFunc(KB_RELEASE, gMatrix[pressRow][pressCol]);
			clicked = NULL;
			PIO_interrupt_init(module, columnPin, PIO_IRQ_FALLING_EDGE, _KEYBOARD_irq);
		}
	}
	else if(!clicked) {
		clicked = module;

		for(uint8_t i = 0; i < KB_COL_COUNT; ++i) {
			if(module == gCol + i) {
				pressCol = i;
			}
			PIO_interrupt_disable(gCol + i);
		}

		PIO_filter_disable(columnPin);

		for(uint8_t i = 0; i < KB_ROW_COUNT; ++i) {
			PIO_setpin_high(gRow[i]);
			while(!PIO_pin_level(gRow[i]));
			if(PIO_pin_level(columnPin)) {
				PIO_interrupt_init(module, PIO(module->port, module->pin), PIO_IRQ_RISING_EDGE, _KEYBOARD_irq);
				pressRow = i;
				PIO_setpin_low(gRow[i]);
				while(PIO_pin_level(gRow[i]));
				break;
			}
			PIO_setpin_low(gRow[i]);
		}

		PIO_filter_debouncer_enable(columnPin, 10);

		gCallbackFunc(KB_PRESS, gMatrix[pressRow][pressCol]);

		for(uint8_t i = 0; i < KB_COL_COUNT; ++i) {
			PIO_interrupt_enable(gCol + i);
		}
	}
}

void KEYBOARD_init(keyboard_cb callback)
{
	/* Check NULL pointers */
	Assert(callback);

	/* Set callback function */
	gCallbackFunc = callback;

	/* Save row pins */
	const uint8_t row[] = KB_ROW_PINS;
	for(uint8_t i = 0; i < KB_ROW_COUNT; ++i) {
		gRow[i] = row[i];
	}

	/* Save pin codes for columns and enable interrupts */
	const uint8_t col[] = KB_COL_PINS;
	for(uint8_t i = 0; i < KB_COL_COUNT; ++i) {
		PIO_filter_debouncer_enable(col[i], 10);
		PIO_interrupt_init(gCol + i, col[i], PIO_IRQ_FALLING_EDGE, _KEYBOARD_irq);
		PIO_interrupt_enable(gCol + i);
	}
}