/**
 * @file uart_user.c
 * @brief User UART routine
 *
 * @section License
 *
 * Copyright (C) 2021-2026 Oryx Embedded SARL. All rights reserved.
 * 
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 2.6.0
 **/

//Dependencies
#include "stm32h7xx.h"
#include "stm32h7xx_hal.h"
#include "os_port.h"
#include "update/update.h"
#include "uart_user.h"
#include "common.h"
#include "ymodem.h"
#include "core/cboot_error.h"
#include "debug.h"
#include "helper.h"

//Y-Modem uart handler
UART_HandleTypeDef UartHandle;

//CycloneBOOT Iap Context
extern UpdateContext updateContext;
//CycloneBOOT Iap Settings
extern UpdateSettings updateSettings;

//IAP mutex handler
extern OsMutex updateMutex;

//Extern Y-Modem filename buffer holder
extern uint8_t aFileName[FILE_NAME_LENGTH];

//Extern update signature public key declaration
extern const uint8_t* pemUpdtSignPublicKey;
extern size_t pemUpdtSignPublicKeyLen;


/**
 * @brief Download a file via serial port
 **/

void uartUserDownload(void)
{
   uint8_t number[11] = {0};
   uint32_t size = 0;
   COM_StatusTypeDef result;

   cboot_error_t cboot_error;

   //Get exclusive access
   osAcquireMutex(&updateMutex);

   //Suspend scheduler
   osSuspendAllTasks();

   //Debug message
   TRACE_INFO("\r\n");
   TRACE_INFO("Starting firmware update...\r\n");

   //Initialize IAP Application context
   cboot_error = updateInit(&updateContext, &updateSettings);
   //Is any error?
   if(cboot_error != CBOOT_NO_ERROR)
   {
      //Debug message
      TRACE_ERROR("Failed to initialize update library!\r\n");
   }
   else
   {
      Serial_PutString((uint8_t *)"Waiting for the file to be sent ... (press 'a' to abort)\n\r");
      result = Ymodem_Receive( &size );
      if (result == COM_OK)
      {
         Serial_PutString((uint8_t *)"\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: ");
         Serial_PutString(aFileName);
         Int2Str(number, size);
         Serial_PutString((uint8_t *)"\n\r Size: ");
         Serial_PutString(number);
         Serial_PutString((uint8_t *)" Bytes\r\n");
         Serial_PutString((uint8_t *)"-------------------\n");

         cboot_error = updateFinalize(&updateContext);
         if(cboot_error != CBOOT_NO_ERROR)
         {
            //Debug message
            TRACE_ERROR("Failed to finalize firmware update!\r\n");
         }
         else
         {
            cboot_error = updateReboot(&updateContext);
            if(cboot_error != CBOOT_NO_ERROR)
            {
               //Debug message
               TRACE_ERROR("Failed to reboot!\r\n");
            }
         }
      }
      else if (result == COM_LIMIT)
      {
         Serial_PutString((uint8_t *)"\n\n\rThe image size is higher than the allowed space memory!\n\r");
      }
      else if (result == COM_DATA)
      {
         Serial_PutString((uint8_t *)"\n\n\rVerification failed!\n\r");
      }
      else if (result == COM_ABORT)
      {
         Serial_PutString((uint8_t *)"\r\n\nAborted by user.\n\r");
      }
      else
      {
         Serial_PutString((uint8_t *)"\n\rFailed to receive the file!\n\r");
      }
   }

   //Resume scheduler
   osResumeAllTasks();

   //Release exclusive access
   osReleaseMutex(&updateMutex);
}


/**
 * @brief Y-Modem Receive User callback
 * @param[in] data Pointer to the received data
 * @param[in] length Length of the received data
 * @retuen Error code
 **/

cboot_error_t Ymodem_Receive_UserCallback(uint8_t *data, size_t length)
{
   cboot_error_t cboot_error;

   //Write received bytes in flash
   cboot_error = updateProcess(&updateContext, data, length);
   //Is any error?
   if(cboot_error != CBOOT_NO_ERROR)
   {
      //Debug message
      TRACE_ERROR("Failed to update firmware!\r\n");
   }

   //Status code
   return cboot_error;
}


/**
 * @brief Initialize user uart
 **/

void uartUserInit(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;

   //Enable GPIOB clock
   __HAL_RCC_GPIOB_CLK_ENABLE();
   //Enable USART3 clock
   __HAL_RCC_USART3_CLK_ENABLE();

   //Configure USART3_TX (PB10)
   GPIO_InitStructure.Pin = GPIO_PIN_10;
   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
   GPIO_InitStructure.Pull = GPIO_PULLUP;
   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

   //Configure USART3_RX (PB11)
   GPIO_InitStructure.Pin = GPIO_PIN_11;
   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
   GPIO_InitStructure.Pull = GPIO_PULLUP;
   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

   //Configure USART3
   UartHandle.Instance = USART3;
   UartHandle.Init.BaudRate = 115200;
   UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
   UartHandle.Init.StopBits = UART_STOPBITS_1;
   UartHandle.Init.Parity = UART_PARITY_NONE;
   UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
   UartHandle.Init.Mode = UART_MODE_TX_RX;
   HAL_UART_Init(&UartHandle);
}


/**
 * @brief Uart user task routine
 **/

void uartUserTask(void *param)
{
   uint8_t key = 0;
   uint8_t state = 0;
   HAL_StatusTypeDef status;

   //Init uart
   uartUserInit();

   while(1)
   {
      if(state == 0)
      {
         Serial_PutString((uint8_t*) "\r\n");
         Serial_PutString((uint8_t*) "=======================\r\n");
         Serial_PutString((uint8_t*) "= Y-MODEM update menu =\r\n");
         Serial_PutString((uint8_t*) "=======================\r\n");
         Serial_PutString((uint8_t*) "Choices:\r\n");
          Serial_PutString((uint8_t*) "  1 -> Download update image\r\n");
         Serial_PutString((uint8_t*) "  2 -> Get bootloader version\r\n");
         Serial_PutString((uint8_t*) "\r\n");

         //Change state
         state = 1;
      }
      else if(state == 1)
      {
         /* Clean the input path */
         __HAL_UART_FLUSH_DRREGISTER(&UartHandle);

         /* Receive key */
         status = HAL_UART_Receive(&UartHandle, &key, 1, RX_TIMEOUT);

         if(status == HAL_OK)
         {
            switch(key)
            {
               case '1':
                  Serial_PutString((uint8_t*) "Downloading update image...\r\n");
                  //Start downloading file
                  uartUserDownload();
                  break;

               case '2':
            	   Serial_PutString((uint8_t*) "1.0.0");
                  Serial_PutString((uint8_t*) "\r\n");
                  break;

               default:
                  Serial_PutString((uint8_t*) "Unknown number!\r\n");
                  Serial_PutString((uint8_t*) "Number should be 1 or 2!\r\n");
                  break;
            }
            Serial_PutString((uint8_t*) "\r\n");

            //Change state
            state = 0;
         }
      }
      else
      {
         //For sanity
      }

      //Os delay
      osDelayTask(200);
   }
}
