/* UA1ZBE Custom Firmware - Mode Dispatcher Implementation
 *
 * Central dispatcher for VFO, POCSAG, Spectrum, and FM modes.
 *
 * Architecture:
 * - VFO mode: use full APP_TimeSlice infrastructure
 * - POCSAG mode: use APP_TimeSlice for interrupts but intercept keys
 * - Spectrum: blocking loop, returns to VFO
 * - FM: uses FM radio subsystem
 */

#include "mode.h"
#include "app/app.h"
#include "app/common.h"
#include "app/display_rssi.h"
#include "driver/bk4819.h"
#include "driver/keyboard.h"
#include "driver/st7565.h"
#include "driver/system.h"
#include "ui/helper.h"
#include "ui/main.h"
#include "misc.h"
#include "radio.h"
#include "settings.h"
#include "functions.h"
#include <string.h>
#include <stdio.h>

#ifdef ENABLE_POCSAG
#include "pocsag/pocsag.h"
#endif

#ifdef ENABLE_SPECTRUM
#include "app/spectrum.h"
#endif

#ifdef ENABLE_FMRADIO
#include "app/fm.h"
#endif

/* === State === */

static op_mode_t s_current_mode = MODE_VFO;
static GUI_DisplayType_t s_saved_display_type;

/* === Mode entry/exit === */

static void mode_enter_vfo(void)
{
    RADIO_SetupRegisters(true);
    gScreenToDisplay = s_saved_display_type;
    gUpdateDisplay = true;
}

static void mode_enter_pocsag(void)
{
#ifdef ENABLE_POCSAG
    POCSAG_ConfigureRadio(gRxVfo->freq_config_RX.Frequency);
    POCSAG_Init(POCSAG_BAUD_1200);

    /* Save current display type and set to INVALID to skip standard key handling */
    s_saved_display_type = gScreenToDisplay;
    gScreenToDisplay = DISPLAY_INVALID;

    ST7565_FillScreen(0x00);
    for (int line = 0; line < FRAME_LINES; line++)
        memset(gFrameBuffer[line], 0, LCD_WIDTH);
    memset(gStatusLine, 0, sizeof(gStatusLine));
#endif
}

static void mode_enter_fm(void)
{
#ifdef ENABLE_FMRADIO
    s_saved_display_type = gScreenToDisplay;
    gFmRadioMode = false;
#endif
}

static void mode_exit_current(void)
{
    switch (s_current_mode) {
    case MODE_POCSAG:
#ifdef ENABLE_POCSAG
        POCSAG_Stop();
        gScreenToDisplay = s_saved_display_type;
#endif
        break;
    case MODE_FM:
#ifdef ENABLE_FMRADIO
        FM_TurnOff();
        gFmRadioMode = false;
        gScreenToDisplay = s_saved_display_type;
#endif
        break;
    default:
        break;
    }
}

/* === Mode switching === */

void MODE_Switch(op_mode_t mode)
{
    if (mode == s_current_mode || mode >= MODE_COUNT)
        return;

    mode_exit_current();
    s_current_mode = mode;

    switch (mode) {
    case MODE_VFO:       mode_enter_vfo();       break;
    case MODE_POCSAG:    mode_enter_pocsag();    break;
    case MODE_SPECTRUM:  /* Spectrum is blocking */ break;
    case MODE_FM:        mode_enter_fm();        break;
    default: break;
    }

    gUpdateDisplay = true;
}

op_mode_t MODE_GetCurrent(void)
{
    return s_current_mode;
}

void MODE_Init(void)
{
    s_current_mode = MODE_VFO;
    s_saved_display_type = DISPLAY_MAIN;
    mode_enter_vfo();
}

/* === POCSAG display === */

static void pocsag_draw(void)
{
#ifdef ENABLE_POCSAG
    ST7565_FillScreen(0x00);
    for (int line = 0; line < FRAME_LINES; line++)
        memset(gFrameBuffer[line], 0, LCD_WIDTH);
    memset(gStatusLine, 0, sizeof(gStatusLine));

    char buf[32];

    /* Header */
    uint32_t baud = POCSAG_GetBaud();
    sprintf(buf, "POCSAG %lu", baud);
    UI_PrintString(buf, 0, LCD_WIDTH, 0, 10);

    /* Status */
    const char *state_str = POCSAG_StateString(POCSAG_GetState());
    UI_PrintStringSmallNormal(state_str, 0, LCD_WIDTH, 16);

    if (POCSAG_SignalDetected())
        UI_PrintStringSmallNormal("SIG", 100, LCD_WIDTH, 16);

    /* Message count and stats */
    sprintf(buf, "Msgs:%d W:%lu", gPocsag.msg_count, gPocsag.total_words);
    UI_PrintStringSmallNormal(buf, 0, LCD_WIDTH, 28);

    sprintf(buf, "Corr:%lu", gPocsag.corrected_errors);
    UI_PrintStringSmallNormal(buf, 0, LCD_WIDTH, 38);

    /* RSSI */
    sprintf(buf, "%ddBm", RSSI_GetdBm());
    UI_PrintStringSmallNormal(buf, 0, LCD_WIDTH, 48);

    /* Help */
    UI_PrintStringSmallNormal("F=512/1200 X=VFO", 0, LCD_WIDTH, 56);

    ST7565_BlitStatusLine();
    ST7565_BlitFullScreen();
#endif
}

/* === Key debounce for POCSAG === */

static KEY_Code_t s_last_key = KEY_INVALID;
static uint16_t   s_key_debounce = 0;

static KEY_Code_t read_key_debounced(void)
{
    KEY_Code_t key = KEYBOARD_Poll();

    if (key == s_last_key) {
        if (s_key_debounce < 3) {
            s_key_debounce++;
            return KEY_INVALID;
        }
    } else {
        s_last_key = key;
        s_key_debounce = 0;
        return KEY_INVALID;
    }

    return key;
}

/* === Timeslice handlers === */

void MODE_TimeSlice10ms(void)
{
    /* Always call standard app processing for interrupts, etc. */
    APP_TimeSlice10ms();

    /* Mode-specific processing */
    switch (s_current_mode) {
    case MODE_VFO:
        /* VFO display already handled by APP_TimeSlice10ms.
         * Add RSSI overlay on top. */
        if (gScreenToDisplay == DISPLAY_MAIN) {
            RSSI_Draw(95, 0, false);
            ST7565_BlitFullScreen();
        }
        break;

    case MODE_POCSAG:
#ifdef ENABLE_POCSAG
        /* Feed audio samples to POCSAG decoder */
        {
            uint16_t audio = BK4819_GetVoiceAmplitudeOut();
            POCSAG_FeedSample(audio);
        }
        POCSAG_Process();

        /* Check for messages */
        if (POCSAG_MessageAvailable()) {
            pocsag_msg_t msg;
            if (POCSAG_GetMessage(&msg)) {
                /* New message received */
            }
        }

        /* Process POCSAG-specific keys (gScreenToDisplay is INVALID) */
        {
            KEY_Code_t Key = read_key_debounced();
            if (Key != KEY_INVALID) {
                if (Key == KEY_EXIT) {
                    MODE_Switch(MODE_VFO);
                } else if (Key == KEY_F || Key == KEY_STAR) {
                    uint32_t cur = POCSAG_GetBaud();
                    POCSAG_SwitchBaud(cur == POCSAG_BAUD_1200
                                      ? POCSAG_BAUD_512
                                      : POCSAG_BAUD_1200);
                }
            }
        }

        /* Redraw POCSAG display periodically */
        {
            static uint16_t rc = 0;
            if (++rc >= 5) {
                rc = 0;
                pocsag_draw();
            }
        }
#endif
        break;

    case MODE_SPECTRUM:
        /* Spectrum has returned — go back to VFO */
        MODE_Switch(MODE_VFO);
        break;

    case MODE_FM:
#ifdef ENABLE_FMRADIO
        if (!gFmRadioMode)
            MODE_Switch(MODE_VFO);
#endif
        break;

    default:
        break;
    }
}

void MODE_TimeSlice500ms(void)
{
    switch (s_current_mode) {
    case MODE_VFO:
        APP_TimeSlice500ms();
        break;
    default:
        break;
    }
}

/* === External key processing hook === */

bool MODE_ProcessKey(int key, bool pressed, bool held)
{
    KEY_Code_t k = (KEY_Code_t)key;

    if (k == KEY_EXIT && pressed && !held) {
        if (s_current_mode != MODE_VFO) {
            MODE_Switch(MODE_VFO);
            return true;
        }
        return false;
    }

    if (pressed && !held) {
        switch (s_current_mode) {
        case MODE_VFO:
            if (k == KEY_SIDE2) {
#ifdef ENABLE_POCSAG
                MODE_Switch(MODE_POCSAG);
                return true;
#endif
            }
            if (k == KEY_SIDE1) {
#ifdef ENABLE_SPECTRUM
                MODE_Switch(MODE_SPECTRUM);
                return true;
#endif
            }
            if (k == KEY_0) {
#ifdef ENABLE_FMRADIO
                MODE_Switch(MODE_FM);
                FM_Start();
                return true;
#endif
            }
            return false;

        default:
            return false;
        }
    }

    return false;
}
