/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright (c) 2016-2021, NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "stdio.h"
#include "Defines.h"
#include "Application.h"
#include "EEPROMAddrs.h"
#include "UserInterface.h"
#include "ComPortDriver.h"
#include "MeteringLPRT.h"
#include "MeteringInterface3Ph.h"
#include "lcd.h"
#include "EEPROMDriver.h"
#include "Timer.h"
#include "PowerModes.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#ifdef THREE_PH_NXP 
#define OverVolLevel      288.0f
#define LowVolLevel       165.0f
#define DispVolLowLevel   165.0f
#define PTMissVolLevel    165.0f
#define PTMissResVolLevel 171.0f
#endif
/*******************************************************************************
* Prototypes
******************************************************************************/
static void lcd_scroll_display(void);
static void On_Phase(uint8 Phase);
static void CheckPhases(void);
static void DisplayActPower(void);
static void DisplayReactPower(void);
static void DisplayAppPower(void);
static void CalculateLCDScrollParam(void);

uint8 AutoScrollParam[MaxAutoDisps] =
{
  DISP_ALL_ON,
  DISP_TIME,
  DISP_DATE,
  DISP_PF,
  DISP_VRMS_PH_R,
  DISP_VRMS_PH_Y,
  DISP_VRMS_PH_B,
  DISP_IRMS_PH_R,
  DISP_IRMS_PH_Y,
  DISP_IRMS_PH_B,
};

uint8 ManualScrollParam[MaxPushDisps] =
{
  DISP_ALL_ON,
  DISP_METER_SNO,
  DISP_DATE,
  DISP_TIME,
  DISP_RYB,
  DISP_VRMS_PH_R,
  DISP_VRMS_PH_Y,
  DISP_VRMS_PH_B,
  DISP_IRMS_PH_R,
  DISP_IRMS_PH_Y,
  DISP_IRMS_PH_B,
  DISP_PF_R,
  DISP_PF_Y,
  DISP_PF_B,
  DISP_PF,
  DISP_FREQ,
  DISP_POWER_ACT_R,
  DISP_POWER_ACT_Y,
  DISP_POWER_ACT_B,
  DISP_POWER_ACT,
};


/*
 * ---------------------------------------
 * LCD Driver Global Variables & Structure
 * ---------------------------------------
 */
/** Load count for Refresh LCD */
vuint16 lcd_refresh_count = 1;
/** Load count for auto scroll of 10 seconds */
vuint16 lcd_scroll_count = 0;

uint8  DispMDTypes;
uint8  DispPowerTypes;
uint8  PowerIndex;
uint8  DisplayMode;
uint8  PhStateToggle;
tDispConfig DispConfig;
stTime Time;
stDate Date;

uint8  DispIndex;
uint8  DispParam = 0x10;

char  disp_string[NUM_DIGITS];
uint8 UIBuffer[MaxPushDisps];
/** Flag for user switch **/
uint8 UpScroll=0;
uint8 DownScroll=0;

/*******************************************************************************
* Code
******************************************************************************/
static void On_Phase(uint8 Phase)
{
  switch (Phase)
  {
  case V_R_PHASE:
    ON_PHASER();
    break;
    
  case V_Y_PHASE:
    ON_PHASEY();
    break;
    
  case V_B_PHASE:
    ON_PHASEB();
    break;
  }
}

/*!
* @brief Intitializes the LCD module in the meter.
*/
void LCDModuleInit(void)
{
  LCDInit();
  LCDClear();
  sprintf(disp_string,"3PHSM");
  lcd_PrintString();
}

/*!
* @brief Initializes the user interface relted data objects.
*/
void UserInterfaceInit(void)
{
  uint16 i;
  uint16 j;
  
  /* Cleanr all LCD segments */
  LCDClear();
  
  /* Turn on all LCD segments, indicative LCD segments are working */
  LCD_ALLFP_ON;
  
  /* Load count for auto scroll of 10 seconds */
  lcd_scroll_count = (10);
  
  /* Load LCD refresh count for 1 */
  lcd_refresh_count = 1;
  
  /* Set the default display modes */
  DisplayMode = DISP_AUTO;
  
  /* Reset a starting display index from a list of display parameter */
  DispIndex = 0x0;
    
  /* Set the display configuration parameters as per application/tender requirement */
  NVReadIIC(DispConfigAddr, (uint8 *)&DispConfig, sizeof(DispConfig));
  if (DispConfig.DispSign != DISPSIGN)
  {
    DispConfig.DispMDTypes = DISP_MDTYPE_kW;
    DispConfig.DispPowerTypes = DISP_POWERTYPE_kW;
    DispConfig.DispSign = DISPSIGN;

    DispConfig.DispAutoSize = 10;
    DispConfig.DispManualSize = 20;
    DispConfig.DispAutoTime = 6;
    DispConfig.DispManualTime = 60;
    
    NVWriteIIC(AutoDispTable, &AutoScrollParam[0], sizeof(AutoScrollParam));
    NVWriteIIC(BattDispTable, &AutoScrollParam[0], sizeof(AutoScrollParam));
    NVWriteIIC(ManualDispTable, &ManualScrollParam[0], sizeof(ManualScrollParam));
    NVWriteIIC(DispConfigAddr, (uint8 *)&DispConfig, sizeof(DispConfig));
  }
  else
  {
    if (SystemState.PowerMode == POWERMODE_RUN)
    {
      // Mains mode
      /* Manual params */
      NVReadIIC(ManualDispTable, UIBuffer, sizeof(ManualScrollParam));
      if (UIBuffer[0] >= 0x10)
      {
        j = 0;
        for (i = 0; i < MaxPushDisps; i++)
        {
          ManualScrollParam[j++] = UIBuffer[i];
        }
      }
      
      /* Auto params */
      NVReadIIC(AutoDispTable, UIBuffer, sizeof(AutoScrollParam));
      if (UIBuffer[0] >= 0x10)
      {
        j = 0;
        for (i = 0; i < MaxAutoDisps; i++)
        {
          AutoScrollParam[j++] = UIBuffer[i];
        }
      }
    }
    else
    {
      NVReadIIC(BattDispTable, UARTBuffer, sizeof(AutoScrollParam));
      if (UIBuffer[0] >= 0x10)
      {
        j = 0;
        for (i = 0; i < MaxAutoDisps; i++)
        {
          AutoScrollParam[j++] = UIBuffer[i];
        }
      }
      else
      {
        NVReadIIC(AutoDispTable, UIBuffer, sizeof(AutoScrollParam));
        if (UIBuffer[0] > 0x10)
        {
          j = 0;
          for (i = 0; i < MaxAutoDisps; i++)
          {
            AutoScrollParam[j++] = UIBuffer[i];
          }
        }
      }
    }
  }

  DispParam = AutoScrollParam[DispIndex];
}

/*!
 * @brief This function LCD display related tasks of the meter.
 */
void Display(void)
{
  if (DownScroll)
  {
    /* Down scroll button press was detected */
    if (SystemState.PowerMode == POWERMODE_BAT)
    {
      BattModeTimeout = BattModeTO;
    }
    else
    { 
      if (DisplayMode == DISP_AUTO)
      {
        DispIndex = 0xFF;
        DisplayMode = DISP_MANUAL;
      }
    }
    CalculateLCDScrollParam();
    DownScroll = 0;
  }
  else if (UpScroll)
  {
    /* Down scroll button press was detected */
    if (SystemState.PowerMode == POWERMODE_BAT)
    {
      /* Battery mode timeout should be reset to keep the meter running */
      BattModeTimeout = BattModeTO;
    }
    
    if (DisplayMode == DISP_AUTO)
    {
      DispIndex = 0x00;
      DisplayMode = DISP_MANUAL;
    }
    
    /* Calculate the scroll index parameter */
    CalculateLCDScrollParam();
    UpScroll = 0;
  }
  else if(0 == lcd_refresh_count)
  {
    lcd_scroll_display();
  }
  else if(0 == lcd_scroll_count)
  {
    if (DisplayMode == DISP_MANUAL)
    {
      DispIndex = 0xFF;
      DisplayMode = DISP_AUTO;
    }
    /* Calculate the scroll index parameter */
    CalculateLCDScrollParam();
  }
}

/*!
* @brief This function calculates the scroll LCD display indices periodically 
* for the scroll display.
*/
void CalculateLCDScrollParam(void)
{
  uint8 *ScreenParam = AutoScrollParam;
  uint8 Size = DispConfig.DispAutoSize;
  
  lcd_scroll_count = DispConfig.DispAutoTime;
  if (DisplayMode == DISP_MANUAL)
  {
    lcd_scroll_count = DispConfig.DispManualTime;
    ScreenParam = ManualScrollParam;
    Size = DispConfig.DispManualSize;
  }
  
  if (UpScroll)
  {
    DispIndex--;
    if ((DispIndex == 0xFF) || (DispIndex >= Size))
      {
        if (SystemState.PowerMode == POWERMODE_BAT)
        {
          TurnLatchOff = TRUE;
        }
        DispIndex = Size - 1;
      }
  }
  else
  {
    DispIndex++;
    if (DispIndex >= Size)
    {
      if (SystemState.PowerMode == POWERMODE_BAT)
      {
        TurnLatchOff = TRUE;
      }
      if (DisplayMode == DISP_AUTO)
      {
        DispIndex = 1;
      }
      else
      {
        DispIndex = 0;
      }
    }
  }
  
  DispParam = ScreenParam[DispIndex];
  lcd_scroll_display();
}

/*!
* @brief This function displays meter parameter message on LCD screen based on 
* a pre-calculated display index, .
*/
void lcd_scroll_display(void)
{
  uint8 Index;
  uint32 temp1 = 0;
  uint32 temp2 = 0;
  
#ifdef EIGHT_BP_LCD
  for (Index = 0; Index < NUM_FRONTPLANEPINS; Index++)
  {
    LCDArray[Index] = 0;
  }
#endif
  LCDClear();
  
  lcd_refresh_count = 1;
  
  if ((SystemState.PowerMode == POWERMODE_RUN) && (MainsOn == TRUE))
  {
    ON_ON();
  }
  
  if ((SystemState.PowerMode == POWERMODE_BAT) || (MainsOn == FALSE))
  {
    ON_BATT();
  }
  for (Index = 0; Index < NUM_DIGITS; Index++)
  {
    disp_string[Index] = 0x20;
  }
  
  switch(DispParam)
  {
  case DISP_ALL_ON:
    LCD_ALLFP_ON;
    if (DisplayMode == DISP_AUTO)
    {
      lcd_scroll_count = 2;
      lcd_refresh_count = 5;
    }
    break;
    
  case DISP_VRMS_PH_R:
  case DISP_VRMS_PH_Y:
  case DISP_VRMS_PH_B:
    Index = DispParam - DISP_VRMS_PH_R;
    temp1 = (uint32)(mlib3phdata.Vrms[Index] * 100);
    sprintf(disp_string," %05ld %01ld  ", temp1, (uint32)(Index+1));
    ON_P3();
    ON_V();
    break;
    
  case DISP_VTHD_PH_R:
  case DISP_VTHD_PH_Y:
  case DISP_VTHD_PH_B:
#ifdef DOFUNDAMENTAL
    Index = DispParam - DISP_VTHD_PH_R;
    sprintf(disp_string,"thd%03d %d",(uint32)(VTHD3Ph[Index] * 10), Index+1);
    ON_V();
#ifdef SEVEN_DIGIT_LCD
    ON_P5();
#else
    ON_P4();
#endif
#endif
    break;
    
  case DISP_IRMS_PH_R:
  case DISP_IRMS_PH_Y:
  case DISP_IRMS_PH_B:
    Index = DispParam - DISP_IRMS_PH_R;
    temp1 = (uint32)(mlib3phdata.Irms[Index] * 1000);
    if ((mlib3phdata.Irms[Index] > 1.0) && ((mlib3phdata.Vrms[Index] > 80.0) && (mlib3phdata.Vrms[Index] < 250.0)))
    {
      temp2 = (uint32)((mlib3phdata.AppPowers[Index] * 1050.0)/mlib3phdata.Vrms[Index]);
      if (temp1 > temp2)
      {
        temp1 = (uint32)((mlib3phdata.AppPowers[Index] * 1000.0)/mlib3phdata.Vrms[Index]);
      }
    }
    sprintf(disp_string," %05ld %01ld  ", temp1,(uint32)(Index+1));
#ifdef THREE_PH_NXP //#ifdef TNEB
    if (mlib3phdata.ISigns[Index] == -1)
    {
      disp_string[0] = '-';
    }
#endif
    ON_A();
    ON_P2();
    break;
    
  case DISP_IRMS_PH_N:  
    sprintf(disp_string," %05ld",(uint32)(mlib3phdata.Irms[3]*1000));
    ON_A();
    ON_P2();
    break;
    
  case DISP_ITHD_PH_R: 
  case DISP_ITHD_PH_Y: 
  case DISP_ITHD_PH_B: 
#ifdef DOFUNDAMENTAL
    Index = DispParam - DISP_ITHD_PH_R;
    sprintf(disp_string,"thd%03d %d",(uint32)(ITHD3Ph[Index] * 10), Index+1);
    ON_A();
#ifdef SEVEN_DIGIT_LCD
    ON_P5();
#else
    ON_P4();
#endif
#endif
    break;
    
  case DISP_POWER_R:
  case DISP_POWER_Y:
  case DISP_POWER_B:
  case DISP_POWER:
    PowerIndex = DispParam - DISP_POWER_R;
    if (DispConfig.DispPowerTypes == DISP_POWERTYPE_kW)
    {
      DisplayActPower();
    }
    else if (DispConfig.DispPowerTypes == DISP_POWERTYPE_kVAr)
    {
      DisplayReactPower();
    }
    else if (DispConfig.DispPowerTypes == DISP_POWERTYPE_kVA)
    {
      DisplayAppPower();
    }
    break;
    
  case DISP_POWER_ACT_R:
  case DISP_POWER_ACT_Y:
  case DISP_POWER_ACT_B:
  case DISP_POWER_ACT:
    PowerIndex = DispParam - DISP_POWER_ACT_R;
    DisplayActPower();
    break; 
    
  case DISP_POWER_REACT_R:
  case DISP_POWER_REACT_Y:
  case DISP_POWER_REACT_B:
  case DISP_POWER_REACT:
    PowerIndex = DispParam - DISP_POWER_REACT_R;
    DisplayReactPower();
    break;
    
  case DISP_POWER_APP_R:
  case DISP_POWER_APP_Y:
  case DISP_POWER_APP_B:
  case DISP_POWER_APP:  
    PowerIndex = DispParam - DISP_POWER_APP_R;
    DisplayAppPower();
    break;
    
  case DISP_FREQ:
    sprintf(disp_string,"   %04ld   ",(uint32)(mlib3phdata.Frequency*100));
    ON_P4();
    ON_HZ();
    break;
    
  case DISP_PF_R:
  case DISP_PF_Y:
  case DISP_PF_B:
    Index = DispParam - DISP_PF_R;
    sprintf(disp_string," %04ld     ",(uint32)(mlib3phdata.PhPowerFactors[Index]*1000));
    if ((mlib3phdata.MetOnImax == FALSE) && (mlib3phdata.PhPowerFactors[Index] > 0.0) && (mlib3phdata.PhPowerFactors[Index] <= 0.98))
    {
      if (mlib3phdata.ReactPowers[Index] >= 0.0)
      {
        disp_string[6] = 'l';
        disp_string[7] = '9';
      }
      else
      {
        disp_string[6] = 'l';
        disp_string[7] = 'd';
      }
    }
    ON_PF();
    ON_P1();
    if (DispParam == DISP_PF_R)
    {
      ON_PR();
    }
    else if(DispParam == DISP_PF_Y)
    {       
      ON_PY();  
    }
    else
      ON_PB();
    break;
    
  case DISP_PF:
    sprintf(disp_string," %04ld     ",(uint32)(mlib3phdata.PowerFactor*1000));
    if ((mlib3phdata.MetOnImax == FALSE) && (mlib3phdata.PowerFactor > 0.0) && (mlib3phdata.PowerFactor <= 0.98))
    {
      //        ON_ONE();
      if (mlib3phdata.Powers[1] >= 0.0)
      {
        disp_string[6] = 'l';
        disp_string[7] = '9';
      }
      else
      {
        disp_string[6] = 'l';
        disp_string[7] = 'd';
      }
    }
    ON_PF();
    ON_P1();
    break;
    
  case DISP_TIME:
    sprintf(disp_string,"%02ld%02ld%02ld  ",(uint32_t)g_stTime.m_ucHr,\
      (uint32_t)g_stTime.m_ucMin, (uint32_t)g_stTime.m_ucSec);
    ON_TIME();
    ON_COL1();
    ON_COL2();
    break;
    
  case DISP_DATE:
    temp1 = g_stDate.m_ucYear - 2000;
    sprintf(disp_string,"%02ld%02ld%02ld", g_stDate.m_ucDay, g_stDate.m_ucMonth, temp1);
    ON_DATE();
    ON_P1();
    ON_P3();
    break; 
    
  case DISP_NS:
    sprintf(disp_string,"nGInILL");
    ON_MAG();
    break;
    
  case DISP_RYB:
    if (PhaseSequence == PHSEQ_FWD)
    {
      sprintf(disp_string, "r y b");
    }
    else
    {
      sprintf(disp_string, "r b y");
    }
    break;
    
  case DISP_METER_SNO:
    NVReadIIC(SRNOAddr, (uint8 *)disp_string, zSRNO);
    break;
    
  case DISP_METER_REVNO:
    sprintf(disp_string,"REU  %3d  ",(uint32)VER_NO);
#ifdef SEVEN_DIGIT_LCD
    //ON_P6();
#else
    ON_P5();
#endif
    break;
    
  case DISP_COMPANY_NAME:
    sprintf(disp_string, "ABC");
    break;
    
  case DISP_MANU_DATE:
    NVReadIIC(MeterManuAddr, (uint8 *)&temp1, 2);
    temp2 = (temp1 >> 8) & 0xFF;
    temp1 &= 0xFF;
    sprintf(disp_string,"MF%02ld%02ld", temp1, temp2);
    ON_DATE();
    ON_P3();
    break;
    
  default:
    break;
  }
  
  if (DispParam != DISP_ALL_ON)
  {
    for (Index = 0; Index < NUM_DIGITS; Index++)
    {
      if (disp_string[Index] == 0)
      {
        disp_string[Index] = 0x20;
      }
    }
   
    lcd_PrintString();
    CheckPhases(); 
  }
  return;
}

void CheckPhases(void)
{
  uint8 i;
#ifdef THREE_PH_NXP 
  PhStateToggle ^= 0x01;
  for (i = 0; i < 3; i++) 
  {
    if (mlib3phdata.Vrms[i] > DispVolLowLevel)
    {
      if (mlib3phdata.Irms[i] > 0.1)
      {
        if (PhStateToggle)
        {
          On_Phase(i);
        }
      }
      else
      {
        On_Phase(i);
      }
    }
  }
#endif
}

static void DisplayActPower(void)
{
  if (PowerIndex < 3)
  {
    sprintf(disp_string," %05ld %01ld  ",(uint32)(mlib3phdata.ActPowers[PowerIndex]),(uint32)(PowerIndex+1));
    if (mlib3phdata.ISigns[PowerIndex] == -1)
    {
      disp_string[0] = '-';
    }
  }
  else
  {
    sprintf(disp_string," %04ld     ",(uint32)(mlib3phdata.Powers[0]/10));
  }
  ON_kW();
  ON_P2();
}

static void DisplayReactPower(void)
{
  if (PowerIndex < 3)
  {
    sprintf(disp_string," %05ld %01ld  ",(uint32)(mlib3phdata.ReactPowers[PowerIndex]),(uint32)(PowerIndex+1));
  }
  else
  {
    sprintf(disp_string," %04ld     ",(uint32)(mlib3phdata.Powers[1]/10));
  }
  ON_kVAr();
  ON_P2();
}

static void DisplayAppPower(void)
{
  if (PowerIndex < 3)
  {
    sprintf(disp_string," %05ld %01ld  ",(uint32)(mlib3phdata.AppPowers[PowerIndex]),(uint32)(PowerIndex+1));
  }
  else
  {
    sprintf(disp_string," %04ld     ",(uint32)(mlib3phdata.Powers[2]/10));
  }
  ON_kVA();
  ON_P2();
}

void DisplayFWActivation(void)
{
  uint8  i;
  
  for (i = 0; i < NUM_DIGITS; i++)
  {
    disp_string[i] = 0;
  }
  for (i = 0; i < NUM_FRONTPLANEPINS; i++)
  {
    LCDArray[i] = 0;
  }
  LCD_ALLFP_OFF;
  sprintf(disp_string,"COM UP");
  lcd_PrintString();
}

void DisplayErase(void)
{
  uint16 i;
  for (i = 0; i < NUM_FRONTPLANEPINS; i++)
  {
    LCDArray[i] = 0;
  }

  LCDClear();
  sprintf(disp_string,"ERASE   ");
  lcd_PrintString();
}

void DisplayDone(void)
{
  uint16 i;
  
  for (i = 0; i < NUM_FRONTPLANEPINS; i++)
  {
    LCDArray[i] = 0;
  }
  LCDClear();
  sprintf(disp_string,"DONE");
  lcd_PrintString();
}