/**
  ******************************************************************************
  * @file    IAP/IAP_Main/Src/menu.c 
  * @author  MCD Application Team

  * @brief   This file provides the software which contains the main menu routine.
  *          The main menu gives the options of:
  *             - downloading a new binary file, 
  *             - uploading internal flash memory,
  *             - executing the binary file already loaded 
  *             - configuring the write protection of the Flash sectors where the 
  *               user loads his binary file.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2017 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/** @addtogroup STM32F4xx_IAP_Main
  * @{
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "common.h"
#include "flash_if.h"
#include "menu.h"
#include "ymodem.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
pFunction JumpToApplication;
uint32_t JumpAddress;
uint32_t FlashProtection = 0;
uint8_t aFileName[FILE_NAME_LENGTH];

/* Private function prototypes -----------------------------------------------*/
//void SerialDownload(void);
COM_StatusTypeDef SerialDownload(void);
void SerialUpload(void);

/* Private functions ---------------------------------------------------------*/
uint8_t Hex2Ascii(uint8_t hex)
{
  if(hex<10)  hex+='0';
  else        hex+='7';
  return hex;
}

void Serail_Ascii_print(uint8_t hex)
{
  uint8_t hex2ascii;

  hex2ascii=Hex2Ascii(hex>>4);
  HAL_UART_Transmit(&huart3, &hex2ascii, 1, RX_TIMEOUT);
  hex2ascii=Hex2Ascii(hex&0x0F);
  HAL_UART_Transmit(&huart3, &hex2ascii, 1, RX_TIMEOUT);

}

void Serial_Dump(uint32_t addr)
{
  uint8_t paddr;
  for(uint8_t n=0; n<16; n++) {
    Serial_PutString((uint8_t *)"0x");
    for(int8_t i=24; i>=0; i-=8) {
      paddr=addr>>i;
      Serail_Ascii_print(paddr);
    }
    Serial_PutString((uint8_t *)"  ");
    for(uint8_t i=0; i<16; i++) {
      Serail_Ascii_print(*(uint8_t*)addr++);
      Serial_PutString((uint8_t *)" ");
      if(i==7)    Serial_PutString((uint8_t *)"- ");
    }
    Serial_PutString((uint8_t *)"\r\n");
  }
  Serial_PutString((uint8_t *)"\r\n\r\n");
}

/**
  * @brief  Download a file via serial port
  * @param  None
  * @retval None
  */
COM_StatusTypeDef SerialDownload(void)
{
  uint8_t number[11] = {0};
  uint32_t size = 0;
  COM_StatusTypeDef result;

  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");
    return COM_OK;
  }
  else if (result == COM_LIMIT)
  {
    Serial_PutString((uint8_t *)"\n\n\rThe image size is higher than the allowed space memory!\n\r");
    return COM_LIMIT;
  }
  else if (result == COM_DATA)
  {
    Serial_PutString((uint8_t *)"\n\n\rVerification failed!\n\r");
    return COM_DATA;
  }
  else if (result == COM_ABORT)
  {
    Serial_PutString((uint8_t *)"\r\n\nAborted by user.\n\r");
    return COM_ABORT;
  }
  else
  {
    Serial_PutString((uint8_t *)"\n\rFailed to receive the file!\n\r");
    return COM_RFAIL;
  }
}

/**
  * @brief  Upload a file via serial port.
  * @param  None
  * @retval None
  */
void SerialUpload(void)
{
  uint8_t status = 0;

  Serial_PutString((uint8_t *)"\n\n\rSelect Receive File\n\r");

  HAL_UART_Receive(&huart3, &status, 1, RX_TIMEOUT);
  if ( status == CRC16)
  {
    /* Transmit the flash image through ymodem protocol */
    status = Ymodem_Transmit((uint8_t*)APPLICATION_ADDRESS, (const uint8_t*)"UploadedFlashImage.bin", USER_FLASH_SIZE);

    if (status != 0)
    {
      Serial_PutString((uint8_t *)"\n\rError Occurred while Transmitting File\n\r");
    }
    else
    {
      Serial_PutString((uint8_t *)"\n\rFile uploaded successfully \n\r");
    }
  }
}

void SerialMenu(void)
{
  /* Test if any sector of Flash memory where user application will be loaded is write protected */
  FlashProtection = FLASH_If_GetWriteProtectionStatus();

  Serial_PutString((uint8_t *)"\r\n==================== Main Menu =======================\r\n\n");
  Serial_PutString((uint8_t *)"  Dump -------------------------------------------- d\r\n\n");
  Serial_PutString((uint8_t *)"  Erase the flash --------------------------------- e\r\n\n");
  Serial_PutString((uint8_t *)"  Help -------------------------------------------- h\r\n\n");
  Serial_PutString((uint8_t *)"  Download ---------------------------------------- 1\r\n\n");
  Serial_PutString((uint8_t *)"  Upload ------------------------------------------ 2\r\n\n");
  Serial_PutString((uint8_t *)"  Jump to user application ------------------------ 3\r\n\n");
  Serial_PutString((uint8_t *)"======================================================\r\n\n");

}

void SerialErase(void)
{
  uint8_t sector;

  Serial_PutString((uint8_t *)"\r\n=====================================================\r\n");
  Serial_PutString((uint8_t *)"                  ERASE SECTER\r\n\n");
  Serial_PutString((uint8_t *)" Which sector do you want to erase? ( 1~7 or A(all) ) \r\n\n");
  Serial_PutString((uint8_t *)"          [1]  16K     0x04000~0x07FFF\r\n");
  Serial_PutString((uint8_t *)"          [2]  16K     0x08000~0x0BFFF\r\n");
  Serial_PutString((uint8_t *)"          [3]  16K     0x0C000~0x0FFFF\r\n");
  Serial_PutString((uint8_t *)"          [4]  64K     0x10000~0x1FFFF\r\n");
  Serial_PutString((uint8_t *)"          [5] 128K     0x20000~0x3FFFF\r\n");
  Serial_PutString((uint8_t *)"          [6] 128K     0x40000~0x5FFFF\r\n");
  Serial_PutString((uint8_t *)"          [7] 128K     0x60000~0x7FFFF\r\n");
  Serial_PutString((uint8_t *)"          [A] 496K     0x04000~0x7FFFF\r\n");
  Serial_PutString((uint8_t *)"======================================================\r\n\n");

  HAL_UART_Receive(&huart3, &sector, 1, RX_TIMEOUT);

  switch(sector)
  {
  case '1':
    Serial_PutString((uint8_t *)"erased 1 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_1);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case '2':
    Serial_PutString((uint8_t *)"erased 2 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_2);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case '3':
    Serial_PutString((uint8_t *)"erased 3 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_3);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case '4':
    Serial_PutString((uint8_t *)"erased 4 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_4);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case '5':
    Serial_PutString((uint8_t *)"erased 5 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_5);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case '6':
    Serial_PutString((uint8_t *)"erased 6 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_6);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case '7':
    Serial_PutString((uint8_t *)"erased 7 sector\r\n\n");
    FLASH_1Sector_Erase(FLASH_SECTOR_7);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  case 'A':
    Serial_PutString((uint8_t *)"erased all sector\r\n\n");
    FLASH_If_Erase(FLASH_SECTOR_1);
    Serial_PutString((uint8_t *)"end\r\n\n");
    break;
  default :
    break;

  }
}
/**
  * @brief  Display the Main Menu on HyperTerminal
  * @param  None
  * @retval None
  */
void Main_Menu(void)
{
  uint8_t key = 0;
  uint8_t rbt_p[]={0xFF,0xFF,0x02,0xFC,0x04,0};         //Reboot Protocol
  Serial_PutString((uint8_t *)"\r\n=======================================================");
  Serial_PutString((uint8_t *)"\r\n=                   WESPION                           =");
  Serial_PutString((uint8_t *)"\r\n=       In-Application Programming Application        =");
  Serial_PutString((uint8_t *)"\r\n=======================================================");
  Serial_PutString((uint8_t *)"\r\n\r\n");

  static uint8_t index=0;

  while (1)
  {

    /* Clean the input path */
    //__HAL_UART_FLUSH_DRREGISTER(&huart3);

    /* Receive key */


    if(index==0)    Serial_PutString((uint8_t *)"\r\ncommand>> ");
    key=0;
    HAL_UART_Receive(&huart3, &key, 1, RX_TIMEOUT);

    if(key==rbt_p[index]) {
      index++;
      if(index==5)  HAL_NVIC_SystemReset();
      continue;
    }
    else if(index==2 && index==0xFF)  continue;
    else {
      index=0;
    }

    static uint32_t daddr;
    switch (key)
    {
      case 'd':
        {
          uint8_t input_addr[2];
          daddr=ADDR_FLASH_SECTOR_0;
          Serial_PutString((uint8_t *)"\r\n\nEnter address : 0x080_ _000 (00 ~ 7F)\r\n\n");
          for(uint8_t i=0; i<2; i++) {
            HAL_UART_Receive(&huart3, &input_addr[i], 1, RX_TIMEOUT);
            if(IS_CAP_LETTER(input_addr[i]) || IS_LC_LETTER(input_addr[i]))   input_addr[i]=CONVERTHEX_ALPHA(input_addr[i]);
            if(IS_09(input_addr[i]))    input_addr[i]=CONVERTDEC(input_addr[i]);
          }
          daddr+=(input_addr[0]<<16)+(input_addr[1]<<12);
          if(daddr>=0x08080000) {
            Serial_PutString((uint8_t *)"Out of range(08 ~ 7F)\r\n\n");
            break;
          }
          Serial_Dump(daddr);
        }
        break;
      case ' ':
        Serial_PutString((uint8_t *)"\r\n");
        daddr+=0x00000100;
        if(daddr>=FLASH_INVALID_ADDR) {
          daddr=ADDR_FLASH_SECTOR_0;
          Serial_Dump(daddr);
        }
        else {
          Serial_Dump(daddr);
        }
        break;
      case 'e':
        SerialErase();
        break;
      case 'h':
        SerialMenu();
        break;
      case '#':
      case '1':
        /* Download user application in the Flash */
        if(SerialDownload()==COM_OK) {                ///
          if(key=='#')    HAL_NVIC_SystemReset();     ///
        }                                             ///
        break;
      case '2':
        /* Upload user application from the Flash */
        SerialUpload();
        break;
      case '3':
        Serial_PutString((uint8_t *)"\r\n");
        Serial_PutString((uint8_t *)"Start program execution......\r\n\n");
        if(FLASH_Check()==VALID_ERROR) {
          Serial_PutString((uint8_t *)"\r\nYou must be download Firmware again.\r\n");
          continue;
        }
        HAL_RCC_DeInit();
        //HAL_DeInit();
        /* execute the new program */
        JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
        /* Jump to user application */
        JumpToApplication = (pFunction) JumpAddress;
        //JumpToApplication = (pFunction) (*(uint32_t*)(APPLICATION_ADDRESS + 4));
        /* Initialize user application's Stack Pointer */
        __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
        JumpToApplication();
        break;
      default :
        Serial_PutByte(key);
      break;

    }
  }
}

/**
  * @}
  */
