/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright (c) 2016-2021, NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "stdio.h"
#include "fsl_wdog.h"
#include "Defines.h"
#include "ComPortDriver.h"
#include "MeteringLPRT.h"
#include "MeteringInterface3Ph.h"
#include "RTCDriver.h"
#include "Calibration3Ph.h"
#include "Application.h"
#include "EEPROMDriver.h"
#include "UserInterface.h"
#include "PowerModes.h"
#include "Timer.h"
#include "IOControls.h"
#include "math.h"
#include "UpgradeFW.h"
#include "lcd.h"
#include "AppInterface.h"
#include "AppCommon.h"
#include "flash_FTFL.h"
#include "mbedtls/sha1.h"
#include "mbedtls/gcm.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*******************************************************************************
* Prototypes
******************************************************************************/
extern uint16_t __checksum;          // linker defined

uint16 ChkSum;
uint8  UARTBuffer[zUARTBUFFER];
uint16 ResponseBufIndex;
uint16 UartChkSumStartIndex;
uint16 UartChkSumEndIndex;
uint8  MeterUnlocked;
stImageTransfer imageTransfer;
uint8 imageTransferReadCount = 0;
uint8 FirstBreathMask;
uint16 CalibLock;
uint16 DontCheckEOLCount;
uint16 DontCheckEOLTimeout;
uint8 SignInAuthed;
mbedtls_gcm_context gcm;
unsigned char   IV[12] = { 0x41, 0x42, 0x43, 0x4D, 0x45, 0x54, 0x00, 0x00, 0x01, 0x23, 0x45, 0x67}; /* System title(8) + Frame counter(4) */
unsigned char EncKey[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
unsigned char AuthData[17] = {0xA5, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF }; /* Additional data = SC(1) + AK(16) */
char SignInSecret[] = "a1b2c3d4e5f6e7d8";

static void Put2Bytes(uint16 Value);
static void Put4Bytes(uint32 Value);
static void InstantData(void);
static void RTCData(void);
static void ComputeChkSum(void);
static uint8 ASCIItoHex(uint8 Value);
static void CalibLocked(void);
static void CalibStart(void);
/*******************************************************************************
 * Code
 ******************************************************************************/

 /*!
 * @brief Prepares a strign "Locked" to be sent to communication client.
 */
static void CalibLocked(void)
{
  UARTBuffer[ResponseBufIndex++] = 'L';
  UARTBuffer[ResponseBufIndex++] = 'o';
  UARTBuffer[ResponseBufIndex++] = 'c';
  UARTBuffer[ResponseBufIndex++] = 'k';
  UARTBuffer[ResponseBufIndex++] = 'e';
  UARTBuffer[ResponseBufIndex++] = 'd';
}

 /*!
 * @brief Prepares a string "start" to be sent to communication client.
 */
static void CalibStart(void)
{
  UARTBuffer[ResponseBufIndex++] = 'S';
  UARTBuffer[ResponseBufIndex++] = 't';
  UARTBuffer[ResponseBufIndex++] = 'a';
  UARTBuffer[ResponseBufIndex++] = 'r';
  UARTBuffer[ResponseBufIndex++] = 't';
}

 /*!
 * @brief Implements ascii to hex conversion.
 */
static uint8 ASCIItoHex(uint8 Value)
{
  uint8 RetVal;
  if ((Value >= '0') && (Value <= '9'))
  {
    RetVal = Value - '0';
  }
  else if ((Value >= 'A') && (Value <= 'Z'))
  {
    RetVal = Value - '7';
  }
  return (RetVal);
}

 /*!
 * @brief Implements checksum calculation of byte array.
 */
static void ComputeChkSum(void)
{
  uint8 i;
  for (i = UartChkSumStartIndex; i < UartChkSumEndIndex; i++)
  {
    ChkSum += UARTBuffer[i];
  }
  
  UARTBuffer[ResponseBufIndex++] = ChkSum >> 8;
  UARTBuffer[ResponseBufIndex++] = ChkSum;
}

 /*!
 * @brief Implements an unsigned integer 2 bytes value being mapped to 2 characters array order.
 */
static void Put2Bytes(uint16 Value)
{
  UARTBuffer[ResponseBufIndex++] = Value >>  8;
  UARTBuffer[ResponseBufIndex++] = Value & 0xFF;
}

 /*!
 * @brief Copies an unsigned integer 4 bytes value being mapped to 4 characters array order.
 */
static void Put4Bytes(uint32 Value)
{
  UARTBuffer[ResponseBufIndex++] = Value >> 24;
  UARTBuffer[ResponseBufIndex++] = Value >> 16;
  UARTBuffer[ResponseBufIndex++] = Value >>  8;
  UARTBuffer[ResponseBufIndex++] = Value & 0xFF;
}

 /*!
 * @brief Copies RTC date-time structures to communication buffer.
 */
static void RTCData(void)
{
  UARTBuffer[ResponseBufIndex++] = g_stDate.m_ucDay;
  UARTBuffer[ResponseBufIndex++] = g_stDate.m_ucMonth;
  UARTBuffer[ResponseBufIndex++] = (g_stDate.m_ucYear - 2000);
  UARTBuffer[ResponseBufIndex++] = g_stTime.m_ucHr;
  UARTBuffer[ResponseBufIndex++] = g_stTime.m_ucMin;
  UARTBuffer[ResponseBufIndex++] = g_stTime.m_ucSec;
}

 /*!
 * @brief Copies instantaneous data to communicaiton buffer.
 */
void InstantData(void)
{  
  uint8  i; 
  uint32 k = 0;
  
  for (i = 0; i < nVPHASES; i++) 
  {
    k = (uint32)(mlib3phdata.Irms[i] * 1000);
    Put4Bytes(k);
  }
  
  for (i = 0; i < nVPHASES; i++) 
  {
    k = (uint32)(mlib3phdata.Vrms[i] * 1000);
    Put4Bytes(k);
  }
  
  for (i = 0; i < nVPHASES; i++) 
  {
    k = (uint32)(mlib3phdata.ActPowers[i] * 1000);
    Put4Bytes(k);
  }
  
  k = (uint32)(mlib3phdata.Powers[0] * 1000);
  Put4Bytes(k);
  
  for (i = 0; i < nVPHASES; i++) 
  {
    k = (uint32)(mlib3phdata.AppPowers[i] * 1000);
    Put4Bytes(k);
  }
  
  k = (uint32)(mlib3phdata.Powers[2] * 1000);
  Put4Bytes(k);
  
  for (i = 0; i < nVPHASES; i++) 
  {
    k = (uint32)(mlib3phdata.ReactPowers[i] * 1000);
    Put4Bytes(k);
  }
  
  k = (uint32)(mlib3phdata.Powers[1] * 1000);
  Put4Bytes(k);
  
  for (i = 0; i < nVPHASES; i++) 
  {
    k = (uint32)(mlib3phdata.PhPowerFactors[i] * 100);
    Put2Bytes(k);
  }
  
  k = (uint32)(mlib3phdata.PowerFactor * 100);
  Put2Bytes(k);
  
  k = (uint32)(mlib3phdata.Frequency * 100);
  Put2Bytes(k);
  
  k = (uint32)(mlib3phdata.Irms[3] * 1000);
  Put4Bytes(k);
  
  for (i = 0; i < nVPHASES; i++) 
  {
    if (mlib3phdata.ISigns[i] == -1)
    {
      UARTBuffer[ResponseBufIndex++] = 1;
    }
    else
    {
      UARTBuffer[ResponseBufIndex++] = 0;
    }
  }
  
  if (DispConfig.DispSign == DISPSIGN)
  {
    UARTBuffer[ResponseBufIndex++] = 0;
  }
  else
  {
    UARTBuffer[ResponseBufIndex++] = 1;
  }
  
  if (g_stDate.m_ucYear < 2050)
  {
    UARTBuffer[ResponseBufIndex++] = 0;
    UARTBuffer[ResponseBufIndex++] = 0;
  }
  else
  {
    UARTBuffer[ResponseBufIndex++] = 1;
    UARTBuffer[ResponseBufIndex++] = 1;
  }
  UARTBuffer[ResponseBufIndex++] = PhaseSequence;
  
  for (i = 0; i < nVPHASES; i++) 
  {
    if ((mlib3phdata.MetOnImax == FALSE) && (mlib3phdata.PhPowerFactors[i] > 0.0) && (mlib3phdata.PhPowerFactors[i] <= 0.98))
    {
      if (mlib3phdata.ReactPowers[i] >= 0.0)
      {
        UARTBuffer[ResponseBufIndex++] = 1;
      }
      else
      {
        UARTBuffer[ResponseBufIndex++] = 0xFF;
      }
    }
    else
    {
      UARTBuffer[ResponseBufIndex++] = 0;
    }
  }  
}

/*!
 * @brief This function/task takes care of all communication specific sub-activities.
 * - Checks whether the meter is powered up enough with supply voltage to sustain communication
 * - Executes any propriotory command for the meter and responds to the communication client.
 */
void Communication(void)
{
  uint8  i;
  uint8  DoChkSum = FALSE;
  uint32 Address;
  uint32 UTCTime;
  uint32 file_checksum = 0;  
  int16 ret_val;  
  uint8 shaBuf[8+16];
  uint8 shaData[20];
  
  
  /* Do it the first job in this function: Check if E_IMAGE_TS_ACTIVATION_INITIATED (and could not finish earlier),
  then CommsModule is not copied fully, so needs to be reattempted now */
  if(imageTransfer.ImageTransferStatus == E_IMAGE_TS_ACTIVATION_INITIATED)
  {
    /* boot mode enale */
    IRTC_SetWriteProtection(RTC, false);
    /* Set Flag in IRTC RAM before resetting the MCU to enter in bootloader mode */
    IRTC_BbRam[0] = 0xBA;
    /* Reset MCU so that bootloader can execute if pre-loaded in the meter */
    IRTC_SetWriteProtection(RTC, true);
    LATCH_OFF;
    NVIC_SystemReset();
  }
  
  /* Current time */
  RTCToUTC(g_stDate, g_stTime, &UTCTime);
  
  if((imageTransfer.ActiveImageUTCTime != 0) && (UTCTime >= imageTransfer.ActiveImageUTCTime))
  {
    ret_val = -1;
    /* if verification was not initiated, then do verification */
    if((imageTransfer.ImageTransferStatus == E_IMAGE_TS_INITIATED)&&
       (imageTransfer.ImageToActivateInfo.Size >0))
     {
       /* make sure entire image is available */ 
       if((((imageTransfer.ImageToActivateInfo.Size-1)/imageTransfer.BlockSize)+1) ==  imageTransfer.ImageFirstNotTransferredBlockNumber)
       {
         /* calculate total checksum now */
         file_checksum = 0;
         VerifyFW(FWBaseAddr, imageTransfer.ImageToActivateInfo.Size, &file_checksum, &ret_val);
         if(ret_val == -1)
         {
           /* verification failed, we need to update status here */
           imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_FAILED;
           imageTransfer.ActiveImageUTCTime =0;
         }
         else
         {
           imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_SUCCESSFUL;
         }
       }
       else
       {
         /* verification failed, we need to update status here */
         imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_FAILED;
         imageTransfer.ActiveImageUTCTime =0;
       }
       NVWriteIIC(ImageTrnfInfoAddr, (void *)&imageTransfer, (sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES));
    }
    
    if((ret_val == 1) ||
       (imageTransfer.ImageTransferStatus == E_IMAGE_TS_VERIFICATION_SUCCESSFUL)&&
         (imageTransfer.ImageToActivateInfo.Size>0))
    {    
      /* check 3 times for imageTransferInfo.ActiveImageUTCTime correctness, 
      as during debug/development phase, incorrect vaule can be read due to incomplete i2c transfer */
      imageTransferReadCount++;
      if(imageTransferReadCount == 3)
      {
        imageTransfer.ImageTransferStatus = E_IMAGE_TS_ACTIVATION_INITIATED;
        imageTransfer.ActiveImageUTCTime =0;
        NVWriteIIC(ImageTrnfInfoAddr, (void *)&imageTransfer, (sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES));
      }
    }
  }
  else
  {
    imageTransferReadCount = 0;
  }

  WDOG_Refresh(WDOG);

  if (SystemState.PowerMode == POWERMODE_BAT)
  {
    if ((UartPortStatus[UART_OPTICAL_INDEX] != UART_IDLE) || (UartPortStatus[UART_ELEC_INDEX] != UART_IDLE))
    {
      BattModeTimeout = BattModeTO;
    }
  }
  
  if (UartPortStatus[UART_OPTICAL_INDEX] != UART_PROCESSING)
  {
    return;
  }
  ResponseBufIndex = 1;
  ChkSum = 0;
  
  /***************************************SETTING START****************************************************/ 
  if (SystemState.PowerMode == POWERMODE_BAT)
  {
    BattModeTimeout = BattModeTO;
  }
  
  if (EEPDefSign != EEPDEFSIGN)
  {
    MeterUnlocked = TRUE;
  }
  
  if (MeterUnlocked == TRUE)
  {
    MeterUnlockTimer = METUNLOCKTO;  
    if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgcsm].cmd_string, metercommand[k_cmdgcsm].size) == 0)
    {
	  /* Copy checksum of the meter sw */
#if defined(__IAR_SYSTEMS_ICC__)
      uint16 *csmPtr = (uint16 *)&__checksum;
#else
      uint16 *csmPtr;// To be done;
#endif
      UARTBuffer[ResponseBufIndex++] = (*csmPtr) >> 8;
      UARTBuffer[ResponseBufIndex++] = (*csmPtr) & 0xFF;
    }

    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdrtccompg].cmd_string, metercommand[k_cmdrtccompg].size) == 0)
    {
      UARTBuffer[ResponseBufIndex++] = CalibStruct3Ph.RTCCompInterval;
      UARTBuffer[ResponseBufIndex++] = CalibStruct3Ph.RTCCompValue;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgsr].cmd_string, metercommand[k_cmdgsr].size) == 0)
    {
      /* Copy meter serial no. to the communicaiton buffer */
      NVReadIIC(SRNOAddr, &UARTBuffer[ResponseBufIndex], zSRNO);
      ResponseBufIndex += zSRNO;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgln].cmd_string, metercommand[k_cmdgln].size) == 0)
    {
      /* Copy meter lot no. to the communicaiton buffer */
      NVReadIIC(LotNoAddr, &UARTBuffer[ResponseBufIndex], zLOTNO);
      ResponseBufIndex += zLOTNO;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgpcb].cmd_string, metercommand[k_cmdgpcb].size) == 0)
    {
      /* Copy meter PCB no. to the communicaiton buffer */
      NVReadIIC(PCBNoAddr, &UARTBuffer[ResponseBufIndex], zPCBNO);
      ResponseBufIndex += zPCBNO;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgnv256].cmd_string, metercommand[k_cmdgnv256].size) == 0)
    {
      /* Copy 256 bytes of EEPROM data from a given address to the communicaiton buffer */
      Address = 0;
      for (i = metercommand[k_cmdgnv256].size; i < (metercommand[k_cmdgnv256].size+5); i++)
      {
        Address <<= 4;
        Address |= ASCIItoHex(UARTBuffer[i]);
      }
      
      NVReadIIC(Address, &UARTBuffer[1], 256);
      ResponseBufIndex = 257;
      UartChkSumStartIndex = 1;
      UartChkSumEndIndex = 256;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgnv128].cmd_string, metercommand[k_cmdgnv128].size) == 0)
    {
      /* Copy 128 bytes of EEPROM data from a given address to the communicaiton buffer */
      Address = 0;
      for (i = metercommand[k_cmdgnv128].size; i < (metercommand[k_cmdgnv128].size+5); i++)
      {
        Address <<= 4;
        Address |= ASCIItoHex(UARTBuffer[i]);
      }
      NVReadIIC(Address, &UARTBuffer[1], 128);
      ResponseBufIndex = 129;
      UartChkSumStartIndex = 1;
      UartChkSumEndIndex = 128;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgnv16].cmd_string, metercommand[k_cmdgnv16].size) == 0)
    {
      Address = 0;
      for (i = metercommand[k_cmdgnv16].size; i < (metercommand[k_cmdgnv16].size+5); i++)
      {
        Address <<= 4;
        Address |= ASCIItoHex(UARTBuffer[i]);
      }
      NVReadIIC(Address, &UARTBuffer[1], 16);
      ResponseBufIndex = 17;
      UartChkSumStartIndex = 1;
      UartChkSumEndIndex = 16;
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdginst].cmd_string, metercommand[k_cmdginst].size) == 0)
    {
      /* Copy instantaneous meter parameter data to the communicaiton buffer */
      InstantData();      
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdsmm].cmd_string, metercommand[k_cmdsmm].size) == 0)
    {
      /* Set the meter manufacturing year etc data from the communicaiton buffer */
      UARTBuffer[metercommand[k_cmdsmm].size] &= 0x7F;
      UARTBuffer[metercommand[k_cmdsmm].size+1] &= 0x7F;
      NVWriteIIC(MeterManuAddr, &UARTBuffer[metercommand[k_cmdsmm].size], 2);
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdsln].cmd_string, metercommand[k_cmdsln].size) == 0)
    {
      /* Set the meter lot no. from the communicaiton buffer */
      NVWriteIIC(LotNoAddr, &UARTBuffer[metercommand[k_cmdsln].size], zLOTNO);
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdspcb].cmd_string, metercommand[k_cmdspcb].size) == 0)
    {
      /* Set the meter PCB no. from the communicaiton buffer */
      NVWriteIIC(PCBNoAddr, &UARTBuffer[metercommand[k_cmdspcb].size], zPCBNO);
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdgrtc].cmd_string, metercommand[k_cmdgrtc].size) == 0)
    {
      /* Copy meter RTC values to the communicaiton buffer */
      RTCData();  
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdsrtc].cmd_string, metercommand[k_cmdsrtc].size) == 0)
    {
      /* Set the meter clock from the communicaiton buffer */
      stDate Date;
      stTime Time;
      for (i = metercommand[k_cmdsrtc].size; i < (metercommand[k_cmdsrtc].size+6); i++)
      {
        UARTBuffer[i] &= 0x7F;
      }
      if ((UARTBuffer[metercommand[k_cmdsrtc].size] < 30) &&
          ((UARTBuffer[metercommand[k_cmdsrtc].size+1] > 0) && (UARTBuffer[metercommand[k_cmdsrtc].size+1] <= 12)) &&
            ((UARTBuffer[metercommand[k_cmdsrtc].size+2] > 0) && (UARTBuffer[metercommand[k_cmdsrtc].size+2] <= 31)) &&
              (UARTBuffer[metercommand[k_cmdsrtc].size+3] < 24) &&
                (UARTBuffer[metercommand[k_cmdsrtc].size+4] < 60) &&
                  (UARTBuffer[metercommand[k_cmdsrtc].size+5] < 60))   
      {
        Date.m_ucYear  = 2000 + UARTBuffer[metercommand[k_cmdsrtc].size];
        Date.m_ucMonth = UARTBuffer[metercommand[k_cmdsrtc].size+1];
        Date.m_ucDay   = UARTBuffer[metercommand[k_cmdsrtc].size+2];
        Time.m_ucHr    = UARTBuffer[metercommand[k_cmdsrtc].size+3];
        Time.m_ucMin   = UARTBuffer[metercommand[k_cmdsrtc].size+4];
        Time.m_ucSec   = UARTBuffer[metercommand[k_cmdsrtc].size+5];
        SetDateAndTime(Date, Time);
        RTCToUTC(Date, Time, &Address);
        NVWriteIIC(LastPON, (uint8 *)&Address, 4);
      }
      else
      {
        UARTBuffer[ResponseBufIndex++] = 'N';
      }
      UARTBuffer[ResponseBufIndex++] = 'O';
      UARTBuffer[ResponseBufIndex++] = 'K';
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdrtccomps].cmd_string, metercommand[k_cmdrtccomps].size) == 0)
    {
      /* RTC crystal Compensation values */
      CalibStruct3Ph.RTCCompInterval = UARTBuffer[metercommand[k_cmdrtccomps].size];
      CalibStruct3Ph.RTCCompValue = (int8)UARTBuffer[metercommand[k_cmdrtccomps].size+1];
      CalibMemwrite3Ph();
      ApplyRTCCompensation(CalibStruct3Ph.RTCCompInterval, CalibStruct3Ph.RTCCompValue);
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdmer].cmd_string, metercommand[k_cmdmer].size) == 0)
    {
      MemErase();
      NVIC_SystemReset();
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdscall].cmd_string, metercommand[k_cmdscall].size) == 0)
    { 
      /* Set calibration mode to locked/unlocked */
      i = UARTBuffer[metercommand[k_cmdscall].size] - '0';
      if (i == 0)
      {
        /* Unlock */
        CalibLock = 0;
      }
      if(i == 1)
      {
        /* Lock */
        CalibLock = CALIBLOCK;
      }
      UARTBuffer[ResponseBufIndex++] = i;
      NVWriteIIC(CALIBLOCKADDR, (uint8 *)&CalibLock, 2); 
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdcalp].cmd_string, metercommand[k_cmdcalp].size) == 0)
    {
      if (CalibLock != CALIBLOCK)
      {
        mlib3phdata.CalibState = CALIBSTATE_PROGRESS;
        CalibStep3Ph  = CALIB_SETUP;
        CalibPoint.Voltage = 240.0;
        CalibPoint.Current = 10.0;
        CalibPoint.PhAngle = 1.04719;
        CalibPoint.PowerFactor = 0.5;
        CalibPoint.Frequency = 50.0;
        CalibStart();
      }
      else
      {
        CalibLocked();
      }
    } 
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdcaln].cmd_string, metercommand[k_cmdcaln].size) == 0)
    {
      if (CalibLock != CALIBLOCK)
      {
        mlib3phdata.CalibState = CALIBSTATE_NEU_PROGRESS;
        CalibStep3Ph  = CALIB_NEU_SETUP;
        CalibPoint.Voltage = 240.0;
        CalibPoint.Current = 10.0;
        CalibPoint.PhAngle = 1.04719;
        CalibPoint.PowerFactor = 0.5;
        CalibPoint.Frequency = 50.0;
        CalibStart();
      }
      else
      {
        /* else, respond calibration has been locked/undone */
        CalibLocked();
      }
    }
    
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmddcal].cmd_string, metercommand[k_cmddcal].size) == 0)
    {
      RestoreDefCalib();
      CalibMemwrite3Ph();
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdsignin].cmd_string, metercommand[k_cmdsignin].size) == 0)
    {
      DontCheckEOLCount = 37; // 9 Byte command + 28 Byte CToMPayload
      DontCheckEOLTimeout = 4;
      SignInAuthed = 0; 
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdctompd].cmd_string, metercommand[k_cmdctompd].size) == 0)
    {
      /* metercommand[k_cmdctompd].size onward has RND data followed by SHA-1 20 bytes */
      memset(shaData, 0, 20);
      memcpy((void *)shaBuf, &UARTBuffer[metercommand[k_cmdctompd].size], 8);
      memcpy((void *)&shaBuf[8], SignInSecret, 16);       /* sign-in authentication key */
      mbedtls_sha1_ret( shaBuf, (8+16), shaData);
      if(memcmp(shaData, &UARTBuffer[metercommand[k_cmdctompd].size+8], 20) != 0)
      {
        SignInAuthed = 0;
        UARTBuffer[ResponseBufIndex++] = 'N';
        UARTBuffer[ResponseBufIndex++] = 'K';
      }
      else
      {
        SignInAuthed = 1;
        UARTBuffer[ResponseBufIndex++] = 'O';
        UARTBuffer[ResponseBufIndex++] = 'K';
      }
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdfwuinit].cmd_string, metercommand[k_cmdfwuinit].size) == 0)
    {
      if(SignInAuthed)
      {
        DontCheckEOLCount = 27; // 9 Bytes k_cmdfwuhdr command + 18 Byte Header
        DontCheckEOLTimeout = 4;
      }
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdfwuhdr].cmd_string, metercommand[k_cmdfwuhdr].size) == 0)
    {
      if(SignInAuthed)
      {
        memset(imageTransfer.ImageTransferredBlocksStatus, 0, IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES);
        imageTransfer.ActiveImageUTCTime = 0; /* clear any pending scheduled activation */
        imageTransfer.ImageTransferStatus = E_IMAGE_TS_INITIATED;
        imageTransfer.ImageFirstNotTransferredBlockNumber = 0;
        memcpy(&(imageTransfer.ImageToActivateInfo.Size), &UARTBuffer[metercommand[k_cmdfwuhdr].size], 4);
        memcpy(&(imageTransfer.BlockSize), &UARTBuffer[metercommand[k_cmdfwuhdr].size+4], 4);
        if((imageTransfer.ImageToActivateInfo.Size > MIN_IMAGE_SIZE) && (imageTransfer.ImageToActivateInfo.Size < MAX_IMAGE_SIZE)
           && (imageTransfer.BlockSize == CUSTOM_IMAGE_TRANS_BLOCK_SIZE))
        {
          /* Initiate image transfer*/
          memcpy(imageTransfer.ImageToActivateInfo.Identity, &UARTBuffer[metercommand[k_cmdfwuhdr].size+8], 10);
          imageTransfer.ImageToActivateInfo.IdentityLength = IMG_TX_MAX_IDENT_LEN;
          DontCheckEOLCount = 8 + 1 + 4 + 12 + imageTransfer.BlockSize; // 8 Bytecommand(nxpfwutx)+ 1 bye Auth-Enc-Flasg + 4 Byte Block Number
          DontCheckEOLTimeout = 4;
#ifdef FlashMemFOTA
          /* erase complete OTA flash partition */
          EraseSectors(FlashMemFOTAStartAddr, (FlashMemFOTAEndAddr - FlashMemFOTAStartAddr));   
#endif
        }
        else
        {
          imageTransfer.ImageTransferStatus = E_IMAGE_TS_NOT_INITIATED;
        }
        NVWriteIIC(ImageTrnfInfoAddr , (void *)&imageTransfer, sizeof(imageTransfer));
      }
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdfwutx].cmd_string, metercommand[k_cmdfwutx].size) == 0)
    {
      uint32 imageBlockNumber;
      uint8  isLastBlcok = 0;
      uint16 this_size;
      uint16 totalBlockCount;
      uint32 i, j;
      int ret;
      
      if(SignInAuthed)
      {
        /* image_block_transfer
        *buf_p holds image_block_number, followed by image_block_value
        */
        if((imageTransfer.ImageTransferStatus == E_IMAGE_TS_INITIATED)&&
           (imageTransfer.ImageToActivateInfo.Size >MIN_IMAGE_SIZE))
        {
          memcpy((void *)&imageBlockNumber, &UARTBuffer[metercommand[k_cmdfwutx].size + 1 + 12], 4);
          
          /* Calculate the last block size */
          if((imageBlockNumber + 1)== (imageTransfer.ImageToActivateInfo.Size / imageTransfer.BlockSize))
          { 
            /* Pre Last Block */
            DontCheckEOLCount = metercommand[k_cmdfwutx].size + 1 + 12 + 4 + (imageTransfer.ImageToActivateInfo.Size - (imageTransfer.ImageToActivateInfo.Size / imageTransfer.BlockSize) * imageTransfer.BlockSize);
          }
          else
          {
            DontCheckEOLCount = metercommand[k_cmdfwutx].size + 1 + 12 + 4 + imageTransfer.BlockSize; // 8 Bytecommand(nxpfwutx)+ 1 bye Auth-Enc-Flasg + 4 Byte Block Number
          }
          
          if((imageBlockNumber)== (imageTransfer.ImageToActivateInfo.Size / imageTransfer.BlockSize))
          {
            /* Last Block */
            this_size = imageTransfer.ImageToActivateInfo.Size - (imageBlockNumber*imageTransfer.BlockSize);
            DontCheckEOLCount = 0;
          }
          else
          {
            this_size = imageTransfer.BlockSize;
          }
          DontCheckEOLTimeout = 4;
          
          /* AES-GCM-128 : Check authentical Tag and then decrypt  */
          mbedtls_gcm_init(&gcm);
          
          memset(fw_buffer, 0, FW_BUFFER_LEN);
          mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, EncKey, 128);
          ret = mbedtls_gcm_auth_decrypt( &gcm, this_size, IV, 12, AuthData, 17, &UARTBuffer[metercommand[k_cmdfwutx].size + 1], 12, &UARTBuffer[metercommand[k_cmdfwutx].size + 1 + 12 + 4], fw_buffer);
          mbedtls_gcm_free(&gcm);
          if(ret != 0)
          {
            UARTBuffer[ResponseBufIndex++] = 'N';
            UARTBuffer[ResponseBufIndex++] = 'K';
            DontCheckEOLCount = 0;
          }
          else
          {
#ifdef FlashMemFOTA
            FLASH_Program(&s_flashDriver, (FlashMemFOTAStartAddr + imageBlockNumber*imageTransfer.BlockSize), (uint8*)fw_buffer, imageTransfer.BlockSize);
#else
            NVWriteIIC((FWBaseAddr + imageBlockNumber*imageTransfer.BlockSize), fw_buffer, imageTransfer.BlockSize);  
#endif
            /* corresponding bit in the image_transferred_blocks_status attribute is set to 1 */
            imageTransfer.ImageTransferredBlocksStatus[imageBlockNumber/8] |= 1 << (7-(imageBlockNumber % 8));
            
            /* update image_first_not_transferred_block_number */
            isLastBlcok = 0;    /* to break search loop */
            totalBlockCount = ((imageTransfer.ImageToActivateInfo.Size-1)/imageTransfer.BlockSize)+1;
            for(i=0; i<=((totalBlockCount-1)/8)+1; i++)
            {
              /* that at least 1 bit is set */
              for(j=0; j<8; j++)
              {
                /* start checking starting from MSB */
                if((imageTransfer.ImageTransferredBlocksStatus[i] & 1 << (7-j)) == 0)
                {
                  imageTransfer.ImageFirstNotTransferredBlockNumber = i*8 + j;
                  isLastBlcok = 1;     /* break the outer loop also */
                  break;
                }
              }
              if(isLastBlcok)
              {
                break;
              }
            }
            
            /* update all elements before imageTransferredBlocksStatus */
            NVWriteIIC((ImageTrnfInfoAddr + sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES + (imageTransfer.ImageFirstNotTransferredBlockNumber/8)),
                       (void *)&imageTransfer.ImageTransferredBlocksStatus[imageTransfer.ImageFirstNotTransferredBlockNumber/8], 1);
            
            /* update specific imageTransferredBlocksStatus byte */
            NVWriteIIC((ImageTrnfInfoAddr + sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES + (imageBlockNumber/8)),
                       (void *)&imageTransfer.ImageTransferredBlocksStatus[imageBlockNumber/8], 1);
            /* Let's put a check here */
            if(imageBlockNumber >= (((imageTransfer.ImageToActivateInfo.Size-1)/imageTransfer.BlockSize)+1))
            {
              imageTransfer.ImageFirstNotTransferredBlockNumber = 0xFFFFFFFF; 
            }
            
            NVWriteIIC(ImageTrnfInfoAddr, (void *)&imageTransfer, (sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES));
            
            UARTBuffer[ResponseBufIndex++] = 'O';
            UARTBuffer[ResponseBufIndex++] = 'K';
          }
        }
      }
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdfwuver].cmd_string, metercommand[k_cmdfwuver].size) == 0)
    {
      /* image_verify */
      uint32 file_checksum = 0;
      int16 ret_val;
      
      NVReadIIC(ImageTrnfInfoAddr , (void *)&imageTransfer, sizeof(imageTransfer));
      /* make sure entire image is available, then go for verification */
      /* verification can be done on E_IMAGE_TS_INITIATED or other status provided below condition is met */
      if((imageTransfer.ImageToActivateInfo.Size >MIN_IMAGE_SIZE)&&
        ((((imageTransfer.ImageToActivateInfo.Size-1)/imageTransfer.BlockSize)+1) ==  imageTransfer.ImageFirstNotTransferredBlockNumber))
      {
        imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_INITIATED;
        
        NVWriteIIC(ImageTrnfInfoAddr, (void *)&imageTransfer, (sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES));
#ifdef FlashMemFOTA
        VerifyFW(FlashMemFOTAStartAddr, imageTransfer.ImageToActivateInfo.Size, &file_checksum, &ret_val);
#else
        VerifyFW(FWBaseAddr, imageTransfer.ImageToActivateInfo.Size, &file_checksum, &ret_val);
#endif
        if(ret_val != (-1))
        {
          sprintf((char *)imageTransfer.ImageToActivateInfo.Signature, "%x", file_checksum);
          imageTransfer.ImageToActivateInfo.SigLength = IMG_TX_MAX_SIGN_LEN;
          imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_SUCCESSFUL;
        }
        else
        {
          imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_FAILED;          
        }
      }
      else
      {
        imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_FAILED;
      }
      NVWriteIIC(ImageTrnfInfoAddr, (void *)&imageTransfer, (sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES));
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdfwuact].cmd_string, metercommand[k_cmdfwuact].size) == 0)
    {
      int16 ret_val;
      
      /* image_activate */
      ret_val = -1;
      
      if(SignInAuthed)
      {
        NVReadIIC(ImageTrnfInfoAddr , (void *)&imageTransfer, sizeof(imageTransfer));
        /* if verification was not initiated, make sure entire image is available, then do verification */
        if((imageTransfer.ImageTransferStatus == E_IMAGE_TS_INITIATED)&&
           (imageTransfer.ImageToActivateInfo.Size > MIN_IMAGE_SIZE))
        {
          if((((imageTransfer.ImageToActivateInfo.Size-1)/imageTransfer.BlockSize)+1) ==  imageTransfer.ImageFirstNotTransferredBlockNumber)
          {
            VerifyFW(FWBaseAddr, imageTransfer.ImageToActivateInfo.Size, &file_checksum, &ret_val);
            
            if(ret_val != (-1))
            {
              sprintf((char *)imageTransfer.ImageToActivateInfo.Signature, "%x", file_checksum);
              imageTransfer.ImageToActivateInfo.SigLength = IMG_TX_MAX_SIGN_LEN;
              imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_SUCCESSFUL;
            }
            else
            {
              imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_FAILED; 
            }
          }
          else
          {
            imageTransfer.ImageTransferStatus = E_IMAGE_TS_VERIFICATION_FAILED; 
          }
        }
        /* don't allow activation if verification was unsuccessful */
        if((ret_val == 1) ||
           ((imageTransfer.ImageTransferStatus == E_IMAGE_TS_VERIFICATION_SUCCESSFUL)&&
            (imageTransfer.ImageToActivateInfo.Size >MIN_IMAGE_SIZE)))
        {
          imageTransfer.ImageTransferStatus = E_IMAGE_TS_ACTIVATION_INITIATED; 
        }
        
        NVWriteIIC(ImageTrnfInfoAddr, (void *)&imageTransfer, (sizeof(imageTransfer) - IMAGE_TRANSFREED_BLOCK_STATUS_SIZE_IN_BYTES));
      }
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdfwusts].cmd_string, metercommand[k_cmdfwusts].size) == 0)
    {
      NVReadIIC(ImageTrnfInfoAddr , (void *)&imageTransfer, sizeof(imageTransfer));
      UARTBuffer[ResponseBufIndex++] = imageTransfer.ImageTransferStatus;
      UARTBuffer[ResponseBufIndex++] = (uint8)(imageTransfer.ImageToActivateInfo.IdentityLength >> 8);  /* High byte */
      UARTBuffer[ResponseBufIndex++] = (uint8)(imageTransfer.ImageToActivateInfo.IdentityLength & 0xFF);/* Low byte */
      for(i=0; i<IMG_TX_MAX_IDENT_LEN; i++)
      {
        UARTBuffer[ResponseBufIndex++] = imageTransfer.ImageToActivateInfo.Identity[i];
      }
    }
    else if(strncmp((const char *)UARTBuffer, metercommand[k_cmdunlock].cmd_string, metercommand[k_cmdunlock].size) == 0)
    {
      /* Unlock communicaiton for METUNLOCKTO duration in seconds */
      MeterUnlocked = TRUE;
      MeterUnlockTimer = METUNLOCKTO;
      UARTBuffer[ResponseBufIndex++] = 'U';
      UARTBuffer[ResponseBufIndex++] = 'n';
      CalibLocked();
    }
  }
  else
  {
    if(strncmp((const char *)UARTBuffer, metercommand[k_cmdunlock].cmd_string, metercommand[k_cmdunlock].size) == 0)
    {
      /* Unlock communicaiton for METUNLOCKTO duration in seconds */
      MeterUnlocked = TRUE;
      MeterUnlockTimer = METUNLOCKTO;
      UARTBuffer[ResponseBufIndex++] = 'U';
      UARTBuffer[ResponseBufIndex++] = 'n';
      CalibLocked();
    }
    else
    {
      CalibLocked();
    }
  }  
  
  /*******************************Commands end***********************************/
  if (DoChkSum == TRUE)
  {
    ComputeChkSum();
    UARTBuffer[ResponseBufIndex++] = ChkSum >> 8;
    UARTBuffer[ResponseBufIndex++] = ChkSum;
  }
  UARTBuffer[0] = STX;
  UARTBuffer[ResponseBufIndex++] = ETX;
  UARTBuffer[ResponseBufIndex++] = 0x0D;
  UARTBuffer[ResponseBufIndex++] = 0x0A;
  /* Send the command response to the client */
  UART_Transmit_Optical(UARTBuffer, ResponseBufIndex);
}


