/*******************************************************************************
 * @file             main.c
 *
 * @brief            Fee + Crypto 综合例程：AES-ECB 加密后写入 FEE，读出再 ECB 解密校验
 *
 * @details          使用与 IntegrationAppCrypto 相同的 AES-128 密钥材料；明文 16 字节
 *                   ECB 加密后写入 Fee 块 0 整块（0x100）；读回密文后 ECB 解密，
 *                   与原始明文逐字节比较。Mcu 需打开 RNG/SHA/AES 时钟（本工程 Mcu_PBcfg 已配）。
 ******************************************************************************/

#include "Fee_App.h"
#include "Fee.h"
#include "Fls.h"
#include "Det.h"

#include "CDD_Usart.h"
#include "Mcu.h"
#include "Port.h"

#include "g32a1085.h"
#include "Crypto.h"
#include "Crypto_App.h"

#include <stdio.h>
#include <string.h>

/***************************************************************************
                        LOCAL DEFINES
***************************************************************************/
#define FEECRYPTO_AES_BLOCK_SIZE    16U
/** KeyElementId：1=AES-128，2=AES-192，3=AES-256（与 Crypto 例程一致） */
#define FEECRYPTO_ECB_KEY_ELEMENT   1U

/***************************************************************************
                        LOCAL DATA（与 IntegrationAppCrypto AES-ECB 一致）
***************************************************************************/
static const uint8 FeeCrypto_Aes128Key[16] = {
    0x35U, 0x35U, 0x35U, 0x35U, 0x36U, 0x36U, 0x36U, 0x36U,
    0x37U, 0x37U, 0x37U, 0x37U, 0x38U, 0x38U, 0x38U, 0x38U
};

static const uint8 FeeCrypto_Aes192Key[24] = {
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U,
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U,
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U
};

static const uint8 FeeCrypto_Aes256Key[32] = {
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U,
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U,
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U,
    0x9AU, 0xBCU, 0xDEU, 0xF0U, 0x9AU, 0xBCU, 0xDEU, 0xF0U
};

/***************************************************************************
                        LOCAL FUNCTIONS (Fee)
***************************************************************************/
static void Fee_WaitTask(MemIf_JobResultType job)
{
    while ((Fee_GetJobResult() == job) &&
           (0U == g_detEventErrNum) &&
           (0U == g_detRuntimeEventErrNum))
    {
        Fee_MainFunction();
        Fls_MainFunction();
    }
}

/*!
 * @brief       erase flash
 *
 * @param       stctorNum: stctor number
 *
 * @retval      None
 *
 * @note        Note
 */
static void Fee_FlsEraseSector(uint8 stctorNum)
{
    Fls_Erase(FEETEST_FIRST_SECTOR_ADDR + (FEETEST_FIRST_SECTOR_SIZE * stctorNum), FEETEST_FIRST_SECTOR_SIZE);

    while(Fls_GetStatus() == MEMIF_BUSY)
    {
        Fls_MainFunction();
    }
}

/*!
 * @brief       erase all flash
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        Note
 */
void Fee_EraseAllUsedFlash(void)
{
    uint8 i;
    
    /* erase all lash in cycles */
    for(i = 0; i < FEETEST_FIRST_USED_SECTOR_NUM_MAX; i++)
    {
        Fee_FlsEraseSector(i);
    }
}
/***************************************************************************
                        LOCAL FUNCTIONS (Crypto)
***************************************************************************/
static void PrepareJob(Crypto_JobType* Job,
                       CryptoTestJobType TestJob,
                       uint32 CryptoKeyId,
                       uint32 KeyElementId,
                       const uint8* InputPtr,
                       uint32 InputLength,
                       uint8* OutputPtr,
                       uint32* OutputLengthPtr,
                       Crypto_ProcessingType ProcessingType)
{
    Job->cryptoKeyId = CryptoKeyId;
    Job->KeyElementId = KeyElementId;
    Job->jobPrimitiveInputOutput.inputPtr = InputPtr;
    Job->jobPrimitiveInputOutput.inputLength = InputLength;
    Job->jobPrimitiveInputOutput.outputPtr = OutputPtr;
    Job->jobPrimitiveInputOutput.outputLengthPtr = OutputLengthPtr;

    Crypto_PrimitiveInfoType* primInfo = NULL_PTR;
    switch (TestJob)
    {
        case TEST_ENCRYPT_AES_ECB:
            primInfo = &g_primAesEcbEncrypt;
            break;
        case TEST_DECRYPT_AES_ECB:
            primInfo = &g_primAesEcbDecrypt;
            break;
        default:
            break;
    }

    if (ProcessingType == CRYPTO_PROCESSING_SYNC)
    {
        g_jobPrimSync.primitiveInfo = primInfo;
        Job->jobPrimitiveInfo = &g_jobPrimSync;
    }
    else
    {
        g_jobPrimAsync.primitiveInfo = primInfo;
        Job->jobPrimitiveInfo = &g_jobPrimAsync;
    }
}

static Crypto_JobType* PrepareTestJob(CryptoTestJobType TestJob,
                                      uint32 CryptoKeyId,
                                      uint32 KeyElementId,
                                      const uint8* InputPtr,
                                      uint32 InputLength,
                                      uint8* OutputPtr,
                                      uint32* OutputLengthPtr,
                                      Crypto_ProcessingType ProcessingType)
{
    PrepareJob(&g_cryptoJob1, TestJob, CryptoKeyId, KeyElementId, InputPtr, InputLength,
               OutputPtr, OutputLengthPtr, ProcessingType);
    return &g_cryptoJob1;
}

/** 加载 Crypto 例程中的多长度密钥并置为有效 */
static Std_ReturnType FeeCrypto_SetupAesKeys(void)
{
    if (Crypto_KeyElementSet(CRYPTO_KEY_1, CRYPTO_KEYMATERIAL_256,
                             FeeCrypto_Aes256Key, sizeof(FeeCrypto_Aes256Key)) != E_OK)
    {
        return E_NOT_OK;
    }
    if (Crypto_KeyElementSet(CRYPTO_KEY_1, CRYPTO_KEYMATERIAL_192,
                             FeeCrypto_Aes192Key, sizeof(FeeCrypto_Aes192Key)) != E_OK)
    {
        return E_NOT_OK;
    }
    if (Crypto_KeyElementSet(CRYPTO_KEY_1, CRYPTO_KEYMATERIAL_128,
                             FeeCrypto_Aes128Key, sizeof(FeeCrypto_Aes128Key)) != E_OK)
    {
        return E_NOT_OK;
    }
    if (Crypto_KeySetValid(CRYPTO_KEY_1) != E_OK)
    {
        return E_NOT_OK;
    }
    return E_OK;
}

/***************************************************************************
                        MAIN
***************************************************************************/
int main(void)
{
    uint8 plainText[FEECRYPTO_AES_BLOCK_SIZE];
    uint8 cipherText[FEECRYPTO_AES_BLOCK_SIZE];
    uint8 decryptedText[FEECRYPTO_AES_BLOCK_SIZE];
    uint8 eepWriteBuf[FEETEST_BLOCK0_SIZE];
    uint8 eepReadBuf[FEETEST_BLOCK0_SIZE];
    uint32 outEncLen;
    uint32 outDecLen;
    uint32 i;
    Std_ReturnType apiRet;
    uint8 demoOk = 1U;

    for (i = 0U; i < FEECRYPTO_AES_BLOCK_SIZE; i++)
    {
        plainText[i] = (uint8)i;
    }
    (void)memset(cipherText, 0, sizeof(cipherText));
    (void)memset(decryptedText, 0, sizeof(decryptedText));
    (void)memset(eepWriteBuf, 0, sizeof(eepWriteBuf));
    (void)memset(eepReadBuf, 0, sizeof(eepReadBuf));

    Mcu_Init(&g_mcuConfig);
    Mcu_InitClock(McuClockSettingConfig_0);
    Port_Init(&g_portConfig);
    Usart_Init(g_usartConfg);

    NVIC_EnableIRQ(USART2_IRQn);
    NVIC_EnableIRQ(USART1_IRQn);
    NVIC_EnableIRQ(SHA256_IRQn);
    NVIC_EnableIRQ(AES256_IRQn);
    NVIC_EnableIRQ(TRNG_IRQn);

    printf("\r\n---------- IntegrationAppFeeCrypto (AES-ECB -> FEE -> ECB) Start ----------\r\n");

    Fls_Init(&g_flsConfig);
    Fee_Init(NULL_PTR);
    Fee_WaitTask(MEMIF_JOB_PENDING);

    if (MEMIF_IDLE != Fee_GetStatus())
    {
        demoOk = 0U;
        printf("Fee not IDLE after init.\r\n");
    }

    Crypto_Init(NULL_PTR);

    if (FeeCrypto_SetupAesKeys() != E_OK)
    {
        demoOk = 0U;
        printf("Crypto key setup failed.\r\n");
    }
    else
    {
        outEncLen = sizeof(cipherText);
        apiRet = Crypto_ProcessJob(CRYPTO_DRIVEROBJECT_WITHQUEUE,
                                   PrepareTestJob(TEST_ENCRYPT_AES_ECB,
                                                  CRYPTO_KEY_1,
                                                  FEECRYPTO_ECB_KEY_ELEMENT,
                                                  plainText,
                                                  sizeof(plainText),
                                                  cipherText,
                                                  &outEncLen,
                                                  CRYPTO_PROCESSING_SYNC));
        if (apiRet != E_OK)
        {
            demoOk = 0U;
            printf("TEST_ENCRYPT_AES_ECB failed (ret=%u).\r\n", (unsigned int)apiRet);
        }
        else
        {
            printf("Plaintext:");
            for (i = 0U; i < sizeof(plainText); i++)
            {
                printf(" %02X", plainText[i]);
            }
            printf("\r\nCiphertext:");
            for (i = 0U; i < sizeof(cipherText); i++)
            {
                printf(" %02X", cipherText[i]);
            }
            printf("\r\n");

            (void)memcpy(eepWriteBuf, cipherText, sizeof(cipherText));
        }
    }

    if (1U == demoOk)
    {
        apiRet = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_0, eepWriteBuf);
        if (apiRet == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }
        else
        {
            demoOk = 0U;
            printf("Fee_Write returned E_NOT_OK.\r\n");
        }

        if (MEMIF_IDLE != Fee_GetStatus())
        {
            demoOk = 0U;
        }

        apiRet = Fee_Read(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_0, 0U,
                          eepReadBuf, FEETEST_BLOCK0_SIZE);
        if (apiRet == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }
        else
        {
            demoOk = 0U;
            printf("Fee_Read returned E_NOT_OK.\r\n");
        }

        if (1U == demoOk)
        {
            if (0 != memcmp(eepWriteBuf, eepReadBuf, FEETEST_BLOCK0_SIZE))
            {
                demoOk = 0U;
                printf("FEE raw read-back mismatch.\r\n");
            }
        }

        if (1U == demoOk)
        {
            outDecLen = sizeof(decryptedText);
            apiRet = Crypto_ProcessJob(CRYPTO_DRIVEROBJECT_WITHQUEUE,
                                       PrepareTestJob(TEST_DECRYPT_AES_ECB,
                                                      CRYPTO_KEY_1,
                                                      FEECRYPTO_ECB_KEY_ELEMENT,
                                                      eepReadBuf,
                                                      sizeof(cipherText),
                                                      decryptedText,
                                                      &outDecLen,
                                                      CRYPTO_PROCESSING_SYNC));
            if (apiRet != E_OK)
            {
                demoOk = 0U;
                printf("TEST_DECRYPT_AES_ECB failed (ret=%u).\r\n", (unsigned int)apiRet);
            }
            else if (0 != memcmp(plainText, decryptedText, sizeof(plainText)))
            {
                demoOk = 0U;
                printf("Decrypted data != original plaintext.\r\n");
            }
            else
            {
                printf("Decrypt OK, plaintext matches.\r\n");
            }
        }
    }

    if (1U == demoOk)
    {
        printf("IntegrationAppFeeCrypto: PASS\r\n");
    }
    else
    {
        printf("IntegrationAppFeeCrypto: FAIL\r\n");
    }

    printf("---------- IntegrationAppFeeCrypto End ----------\r\n");

    for (;;)
    {
    }
}

#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

int fputc(int ch, FILE* f)
{
    (void)f;
    return Usart_PutRedefinition(USART1_ID, &ch);
}

size_t __write(int handle, const unsigned char* buf, size_t bufSize)
{
    size_t i;
    (void)handle;
    for (i = 0U; i < bufSize; i++)
    {
        int32 c = (int32)buf[i];
        (void)Usart_PutRedefinition(USART1_ID, &c);
    }
    return bufSize;
}

#elif defined (__GNUC__)

int _write(int fd, const unsigned char* buf, int len)
{
    int i;
    (void)fd;
    for (i = 0; i < len; i++)
    {
        int32 c = (int32)buf[i];
        (void)Usart_PutRedefinition(USART1_ID, &c);
    }
    return len;
}

#endif
