/**
 * Maticová klávesnice 4x4 s přenosem IR na jiné Pico
 * ir_keyboard.c
 * Copyright (c) 2026 Jirka Chráska, <jirka@lixis.cz>
 *
 * License: BSD-3-Clause
 */

#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "pico/util/queue.h"
#include "hardware/pio.h"
// knihovna pro přenos IR
#include "nec_transmit.h"                           
// klávesnice pomocí PIO
#include "keyboard.pio.h"

#define BASE_KEYPAD_PIN 14
#define OUT_KEYPAD_PIN  18
#define KEYROWS         4
#define NUMKEYS         16
#define FIFO_SIZE       64
#define MAX_COUNTER     10
#define IR_addr         0xac
#define IR_LED_PIN      2


static PIO ir_pio = pio0;
static uint ir_sm;

static PIO kbd_pio = pio1;
static uint kbd_sm;
static uint8_t kbd_pio_irq;
static uint kbd_offset;
static bool klavesa = false;
static queue_t fifo;

char pismeno = '\0';


static char klavesy[4][4] = {
                            '1', '2', '3', 'A',
                            '4', '5', '6', 'B',
                            '7', '8', '9', 'C',
                            '*', '0', '#', 'D'
                            };
                            
                            
static uint32_t tx_frame[4][4] = {
        0,0,0,0,
        0,0,0,0,
        0,0,0,0,
        0,0,0,0
};

// naplnění pole tx_frame
// zakódování jednotlivých kláves pro přenos NEC protokolem
void encode_all_keys()
{
    for(int i=0; i<4; i++) {
        for(int j=0; j<4; j++) {
            tx_frame[j][i] = nec_encode_frame(IR_addr, klavesy[i][j]);
        }
    }
}

// obsluha přerušení vyvolaná při stisku klávesy
static void pio_irq_obsluzna_funkce( void )
{
    while( !pio_sm_is_rx_fifo_empty( kbd_pio, kbd_sm ) ) {
        if( pio_sm_get_rx_fifo_level(kbd_pio, kbd_sm) == 2 ) {
            uint32_t x,y;
            x = pio_sm_get( kbd_pio, kbd_sm );
            y = pio_sm_get( kbd_pio, kbd_sm );
            int i, j;
            for( i=0; i<4; i++) {
                if( x >> i == 1) break;
            }
            for( j=0; j<4; j++) {
                if( y >> j == 1 ) break;
            }
            pismeno = klavesy[j][i];
            // odeslání IR kódu
            pio_sm_put(ir_pio, ir_sm, tx_frame[i][j]);
            klavesa = true;
        }
    }
    irq_clear(kbd_pio_irq);
}



int main() 
{
    stdio_init_all();
    gpio_init(25);
    gpio_set_dir(25,true);
    gpio_put(25,1);
    sleep_ms(2000);
    gpio_put(25,0);
    printf("Klávesnice s IR vysílačem.\n");
    // protože budeme posílat IR kódy v rámci obsluhy přerušení klávesnice,
    // připravíme si dopředu zakódování, aby byla obsluha přerušení rychlá
    encode_all_keys();
    const uint tx_gpio = IR_LED_PIN;   // pin kde je IR LED

    // konfigurace a spuštění stavového PIO strojku pro IR
    ir_sm = nec_tx_init(ir_pio, tx_gpio);           
    
    // nastavení klávesnice
    queue_init(&fifo, sizeof(uint32_t), FIFO_SIZE);
    kbd_offset = pio_add_program(kbd_pio, &keyboard_program);
    kbd_sm = pio_claim_unused_sm(kbd_pio, true);
    if (kbd_sm == -1 || ir_sm == -1) {
        printf("Nemohu nastavit PIO\n");
        return -1;
    }
    keyboard_program_init(kbd_pio, kbd_sm, kbd_offset, BASE_KEYPAD_PIN, OUT_KEYPAD_PIN);
    // najdeme volné přerušení
    kbd_pio_irq = pio_get_irq_num(kbd_pio,0);
    if( irq_get_exclusive_handler( kbd_pio_irq ) ) {
        kbd_pio_irq++;
        if( irq_get_exclusive_handler( kbd_pio_irq ) ){
            panic("Nenalezen volný interrupt.");
        }
    }
    // nastavíme přerušení pro klávesnici
    // přidáme sdílenou obsluhu
    irq_add_shared_handler(kbd_pio_irq, pio_irq_obsluzna_funkce, 
                           PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); 
    irq_set_enabled(kbd_pio_irq, true); // spustíme
    // obdržíme číslo přerušení
    const uint irq_index = kbd_pio_irq - pio_get_irq_num( kbd_pio, 0); 
    // nastavíme pio, aby nám řekl, když je něco ve frontě
    pio_set_irqn_source_enabled( kbd_pio, irq_index, 
                                 pio_get_rx_fifo_not_empty_interrupt_source(kbd_sm), true); 
                          
    // tohle je jenom test, že to funguje
    while (true) {      
        if(klavesa == true) {
            gpio_put(25,1); // blikneme default LEDkou
            printf("Stisknuto '%c'\n",pismeno);
            klavesa = false;
            sleep_ms(200);
            gpio_put(25,0);
        }
        sleep_ms(100);
    }
    
    queue_free(&fifo);
}
