/* Copyright 2023 Dual Tachyon
 * https://github.com/DualTachyon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 */

#ifdef ENABLE_AIRCOPY

//#if !defined(ENABLE_OVERLAY)
//  #include "ARMCM0.h"
//#endif

#include "app/aircopy.h"
#include "audio.h"
#include "driver/bk4819.h"
#include "driver/crc.h"
#include "driver/eeprom.h"
#include "frequencies.h"
#include "misc.h"
#include "radio.h"
#include "ui/helper.h"
#include "ui/inputbox.h"
#include "ui/ui.h"

#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
#include "screenshot.h"
#endif

static const uint16_t Obfuscation[8] = {0x6C16, 0xE614, 0x912E, 0x400D, 0x3521, 0x40D5, 0x0313, 0x80E9};

AIRCOPY_State_t gAircopyState;
uint16_t gAirCopyBlockNumber;
uint16_t gErrorsDuringAirCopy;
uint8_t gAirCopyIsSendMode;

uint16_t g_FSK_Buffer[36];

static void AIRCOPY_clear() {
    for (uint8_t i = 0; i < 15; i++) {
        crc[i] = 0;
    }
}

bool AIRCOPY_SendMessage(void) {
    static uint8_t gAircopySendCountdown = 1;

    if (gAircopyState != AIRCOPY_TRANSFER) {
        return 1;
    }

    if (--gAircopySendCountdown) {
        return 1;
    }

    g_FSK_Buffer[1] = (gAirCopyBlockNumber & 0x3FF) << 6;

    EEPROM_ReadBuffer(g_FSK_Buffer[1], &g_FSK_Buffer[2], 64);

    g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);

    for (unsigned int i = 0; i < 34; i++) {
        g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
    }

    if (++gAirCopyBlockNumber >= 0x78) {
        gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
        getScreenShot();
#endif
        //NVIC_SystemReset();
    }

    RADIO_SetTxParameters();

    BK4819_SendFSKData(g_FSK_Buffer);
    BK4819_SetupPowerAmplifier(0, 0);
    BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);

    gAircopySendCountdown = 30;

    return 0;
}

void AIRCOPY_StorePacket(void) {
    if (gFSKWriteIndex < 36) {
        return;
    }

    gFSKWriteIndex = 0;
    gUpdateDisplay = true;
    uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
    BK4819_PrepareFSKReceive();

    // Doc says bit 4 should be 1 = CRC OK, 0 = CRC FAIL, but original firmware checks for FAIL.

    if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
        gErrorsDuringAirCopy++;
        return;
    }

    for (unsigned int i = 0; i < 34; i++) {
        g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
    }

    uint16_t CRC = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
    if (g_FSK_Buffer[34] != CRC) {
        gErrorsDuringAirCopy++;
        return;
    }

    uint16_t Offset = g_FSK_Buffer[1];

    if (Offset >= 0x1E00) {
        gErrorsDuringAirCopy++;
        return;
    }

    const uint16_t *pData = &g_FSK_Buffer[2];
    for (unsigned int i = 0; i < 8; i++) {
        EEPROM_WriteBuffer(Offset, pData);
        pData += 4;
        Offset += 8;
    }

    if (Offset == 0x1E00) {
        gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
        getScreenShot();
#endif
    }

    gAirCopyBlockNumber++;
}

static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) {
    if (bKeyHeld || !bKeyPressed) {
        return;
    }

    INPUTBOX_Append(Key);

    gRequestDisplayScreen = DISPLAY_AIRCOPY;

    if (gInputBoxIndex < 6) {
#ifdef ENABLE_VOICE
        gAnotherVoiceID = (VOICE_ID_t)Key;
#endif
        return;
    }

    gInputBoxIndex = 0;
    uint32_t Frequency = StrToUL(INPUTBOX_GetAscii()) * 100;

    for (unsigned int i = 0; i < BAND_N_ELEM; i++) {
        if (Frequency < frequencyBandTable[i].lower || Frequency >= frequencyBandTable[i].upper) {
            continue;
        }

        if (TX_freq_check(Frequency)) {
            continue;
        }

#ifdef ENABLE_VOICE
        gAnotherVoiceID = (VOICE_ID_t)Key;
#endif

        Frequency = FREQUENCY_RoundToStep(Frequency, gRxVfo->StepFrequency);
        gRxVfo->Band = i;
        gRxVfo->freq_config_RX.Frequency = Frequency;
        gRxVfo->freq_config_TX.Frequency = Frequency;
        RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
        gCurrentVfo = gRxVfo;
        RADIO_SetupRegisters(true);
        BK4819_SetupAircopy();
        BK4819_ResetFSK();
        return;
    }

    gRequestDisplayScreen = DISPLAY_AIRCOPY;
}

static void AIRCOPY_Key_EXIT(bool bKeyPressed, bool bKeyHeld) {
    if (bKeyHeld || !bKeyPressed) {
        return;
    }

    if (gInputBoxIndex == 0) {
        gAircopyStep = 1;
        gFSKWriteIndex = 0;
        gAirCopyBlockNumber = 0;
        gInputBoxIndex = 0;
        gErrorsDuringAirCopy = lErrorsDuringAirCopy = 0;
        gAirCopyIsSendMode = 0;

        AIRCOPY_clear();

        BK4819_PrepareFSKReceive();

        gAircopyState = AIRCOPY_TRANSFER;
    } else {
        gInputBox[--gInputBoxIndex] = 10;
    }

    gRequestDisplayScreen = DISPLAY_AIRCOPY;
}

static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld) {
    if (bKeyHeld || !bKeyPressed) {
        return;
    }

    gAircopyStep = 1;
    gFSKWriteIndex = 0;
    gAirCopyBlockNumber = 0;
    gInputBoxIndex = 0;
    gAirCopyIsSendMode = 1;
    g_FSK_Buffer[0] = 0xABCD;
    g_FSK_Buffer[1] = 0;
    g_FSK_Buffer[35] = 0xDCBA;

    AIRCOPY_clear();

    GUI_DisplayScreen();

    gAircopyState = AIRCOPY_TRANSFER;
}

void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) {
    switch (Key) {
        case KEY_0:
        case KEY_1:
        case KEY_2:
        case KEY_3:
        case KEY_4:
        case KEY_5:
        case KEY_6:
        case KEY_7:
        case KEY_8:
        case KEY_9:
            AIRCOPY_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
            break;
        case KEY_MENU:
            AIRCOPY_Key_MENU(bKeyPressed, bKeyHeld);
            break;
        case KEY_EXIT:
            AIRCOPY_Key_EXIT(bKeyPressed, bKeyHeld);
            break;
        default:
            break;
    }
}

#endif
