/* kodzamek.c
 * Jednoduchý kódový zámek
 * (c) Jirka Chráska 2026, <jirka@lixis.cz>
 */

#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "ssd1306.h"
#include "font.h"
#include <stdio.h>
#include <string.h>

// pro OLED displej SSD1306
#define DISPLAY_WIDTH   128			// šířka displeje
#define DISPLAY_HEIGHT  32			// výška displeje
#define I2C_ADDRESS     0x3C		// i2c adresa
#define I2C_FREQ        1000000		// rychlost i2c sběrnice
// rychlost i2c sběrnice může být až 2000000
#define SDA_PIN         4			// datový pin
#define SDC_PIN         5			// hodinový pin
#define I2C             i2c0		// i2c hardware

// struktura pro OLED displej včetně framebufferu
static ssd1306_t disp;

// všechny použité fonty
extern bitmapFONT font_spleen_8x16;

#define BASE_KEYPAD_PIN 9
#define KEYROWS         4
#define NUMKEYS         16

// Inicializujeme celou klávesnici
void keypad_init() 
{
    gpio_init_mask((0xFF << BASE_KEYPAD_PIN));
    // Nastavíme řádkové piny (GP9, GP10, GP11 a GP12) jako výstupní
    gpio_set_dir_out_masked((0xF << BASE_KEYPAD_PIN));
    // Nastavíme všechny výstupní piny na nízkou úroveň
    gpio_put_masked((0xF << BASE_KEYPAD_PIN), (0x0 << BASE_KEYPAD_PIN));
    // Zapneme pro vstupní sloupcové piny pull-down rezistory (je to nastaveno implicitně)
    gpio_pull_down((BASE_KEYPAD_PIN + 4));
    gpio_pull_down((BASE_KEYPAD_PIN + 5));
    gpio_pull_down((BASE_KEYPAD_PIN + 6));
    gpio_pull_down((BASE_KEYPAD_PIN + 7));
}

int keypad_read()
{
// klávesa na klávesnici            0     1     2     3     4     5     6     7     8     9     
//                                  A     B     C     D     *     #
unsigned int keycodes[NUMKEYS]  = { 0x28, 0x11, 0x21, 0x41, 0x12, 0x22, 0x42, 0x14, 0x24, 0x44, 
                                    0x81, 0x82, 0x84, 0x88, 0x18, 0x48};
unsigned int scancodes[KEYROWS] = { 0x01, 0x02, 0x04, 0x08} ;
unsigned int button = 0xF0 ;
unsigned int keypad;
int i;

    // Skenujeme klávesnici
    for (i=0; i<KEYROWS; i++) {
        // Nastavíme řádek na jedničku
        gpio_put_masked((0xF << BASE_KEYPAD_PIN), (scancodes[i] << BASE_KEYPAD_PIN));
        // potřebujeme malou prodlevu
        sleep_us(1) ; 
        // Čteme kód klávesy
        keypad = ((gpio_get_all() >> BASE_KEYPAD_PIN) & 0xFF);
        //printf("keypad=%x\n",keypad);
        // Vyskočíme, pokud je nějaké tlačítko stisknuto
        if (keypad & button) break;
    }
    // Pokud jsme našli tlačítko . . .
    if (keypad & button) {
        // Ověříme kód klávesy.
        for (i=0; i<NUMKEYS; i++) {
            if (keypad == keycodes[i]) break ; // podařilo se
        }
        // Pokud se nám ověření klávesy nepodařilo, vrátíme neplatný kód klávesy
        if (i==NUMKEYS) (i = -1);
    }
    // Cokoliv jiného znamená neplatný kód klávesy nebo nestisknuté tlačítko.
    else (i=-1) ;
return i;
}

// nastavení I2C sběrnice
void setup_i2c(void)
{
    i2c_init(I2C, I2C_FREQ);
    gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(SDC_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(SDA_PIN);
    gpio_pull_up(SDC_PIN);
    // pauza je nutná kvůli displeji. Pokud není displej se někdy nerozsvítí
    sleep_ms(200);
}

void zapnuti_displeje( void )
{
    ssd1306_init(&disp, DISPLAY_WIDTH, DISPLAY_HEIGHT, I2C_ADDRESS, I2C); // inicializace
    ssd1306_poweron(&disp); // zapnutí displeje
    ssd1306_clear(&disp);   // vymazání displeje
    ssd1306_contrast(&disp,0x50);
    ssd1306_show(&disp); // zobrazíme na displeji
}

#define BUF_LEN 128
#define ZAMEK_PIN 25

int main()
{
int klavesa = -1;
int predchozi_klavesa = -1;
char c[NUMKEYS] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C' ,'D', '*', '#'};
const char tajne[] = "2931";
char buffer[BUF_LEN+1];
int index = 0;

    memset(buffer, '\0', BUF_LEN+1);
    stdio_init_all();
    sleep_ms(1000);
    printf("Kódový zámek.\n");
    keypad_init();

    // nastavení zámku
    gpio_init(ZAMEK_PIN);
    gpio_set_dir(ZAMEK_PIN,GPIO_OUT);
    gpio_put(ZAMEK_PIN,1);
    sleep_ms(300);
    gpio_put(ZAMEK_PIN,0);

    // nastavení displeje SSD1306
    setup_i2c();
    zapnuti_displeje();
    sleep_ms(10);
    // vymazání framebufferu
	ssd1306_clear(&disp);
    ssd1306_draw_utf8_string(&disp, 0, 0, &font_spleen_8x16, "Zadejte PIN");
    ssd1306_draw_utf8_string(&disp, 0, 16, &font_spleen_8x16, "----");
    ssd1306_show(&disp); // zobrazíme písmenka na displeji
                    
    while( true ) {
        klavesa = keypad_read();
        if( klavesa != -1 && klavesa != predchozi_klavesa ) {
            printf("Stisknuta klávesa %c, index = %d\n",c[klavesa], index);
            predchozi_klavesa = klavesa;    
            if(c[klavesa] == '#' ) { // vynulování bufferu
                index = 0;
                memset(buffer,'\0',BUF_LEN+1);    
            } else if(c[klavesa] == '*') { // výmaz posledního znaku
                if( index > 0 ) {
                    buffer[index-1] = '\0';
                    index--;
                }
            } else {
                if( index < BUF_LEN ) {
                    buffer[index] = c[klavesa];
                    index++;
                    buffer[index] = '\0';
                    // vymazání framebufferu
                } else {
                    index = 0;
                    memset(buffer, '\0', BUF_LEN);
                }
            }
        } 
        else {
            predchozi_klavesa = -1;
        }
        if(strcmp(buffer,tajne) == 0 ) { // otevřít zámek
            ssd1306_clear(&disp);
            ssd1306_draw_utf8_string(&disp, 0, 0, &font_spleen_8x16, "Otevřeno.");
            ssd1306_draw_utf8_string(&disp, 0, 16, &font_spleen_8x16, buffer);
            ssd1306_show(&disp); // zobrazíme písmenka na displeji
            gpio_put(ZAMEK_PIN,1);
        } else {
            ssd1306_clear(&disp);
            ssd1306_draw_utf8_string(&disp, 0, 0, &font_spleen_8x16, "Zadejte PIN");
            if( strlen(buffer) == 0 ) {
                ssd1306_draw_utf8_string(&disp, 0, 16, &font_spleen_8x16, "----");
            } else {
                ssd1306_draw_utf8_string(&disp, 0, 16, &font_spleen_8x16, buffer);
            }
            ssd1306_show(&disp); // zobrazíme písmenka na displeji
            gpio_put(ZAMEK_PIN,0);
        }
        sleep_ms(200);
    }
}
