/*
 * drv_display.c
 *
 *  Created on: 21 февр. 2025 г.
 *      Author: basov.g
 */
#include "drv_display.h"

#include <stdbool.h>
#include <stdint.h>
#include "MDR32VF0xI_port.h"
#include "MDR32VF0xI_ssp.h"
#include "MDR32VF0xI_rst_clk.h"
#include "GF_Library.h"
#include "drv_adcui.h"
#include "drv_clock.h"
#include "drv_adc.h"
#include "drv_flash.h"
#include "../utils/xprintf.h"
#include "../utils/utils.h"
#define LCD_PORT        MDR_PORTA
#define LCD_PORT_CLK    RST_CLK_PCLK_PORTA
#define LCD_CS      	PORT_PIN_0
#define LCD_SCL     	PORT_PIN_1
#define LCD_A0      	PORT_PIN_2
#define LCD_SI      	PORT_PIN_3
#define LCD_RES     	PORT_PIN_4
#define LCD_LED     	PORT_PIN_5
#define LCD_SSP_CLK		RST_CLK_PCLK_SSP2
#define LCD_SSP    		MDR_SSP2

static uint8_t bufferDisplay[128*64/8] = {0};
static enumDisplayPage currentDisplay;
static uint8_t currentSubDisplay = 0;
static uint64_t timeLastDisplayLed = 0;

#define MAINPAGECOUNT	8
uint8_t subPageCount[MAINPAGECOUNT] = {1, 1, 1, 1, 1, 7, 1, 11};


void drv_display_initPins(void)
{
	RST_CLK_PCLKCmd(LCD_PORT_CLK, ENABLE);
	PORT_InitTypeDef PORT_InitStructure;
	PORT_InitStructure.PORT_Direction    	= PORT_DIRECTION_OUTPUT;
	PORT_InitStructure.PORT_Function  		= PORT_FUNCTION_PORT;
	PORT_InitStructure.PORT_Mode  			= PORT_MODE_DIGITAL;
	PORT_InitStructure.PORT_Power 			= PORT_POWER_NOMINAL_UPTO_2mA;
	PORT_InitStructure.PORT_PullDown  		= PORT_PULL_DOWN_OFF;
	PORT_InitStructure.PORT_PullUp  		= PORT_PULL_UP_OFF;
	PORT_InitStructure.PORT_Pin = (LCD_CS | LCD_A0 | LCD_RES | LCD_LED);
	PORT_Init(LCD_PORT, &PORT_InitStructure);

	PORT_InitStructure.PORT_Function  		= PORT_FUNCTION_ALTERNATIVE;
	PORT_InitStructure.PORT_Pin =  (LCD_SCL | LCD_SI);
	PORT_Init(LCD_PORT, &PORT_InitStructure);
}

void drv_display_initSSP(void)
{
	RST_CLK_PCLKCmd(LCD_SSP_CLK, ENABLE);
	RST_CLK_PER1_C2_Cmd(RST_CLK_PER1_C2_SSP2,ENABLE);
	RST_CLK_PER1_C2_SetPrescaler(RST_CLK_PER1_C2_SSP2,RST_CLK_PER1_PRESCALER_DIV_1);

	SSP_DeInit(LCD_SSP);
	SSP_InitTypeDef SSP_InitStructure = {0};

	SSP_InitStructure.SSP_CPSDVSR=16;
	SSP_InitStructure.SSP_SCR=1;

	SSP_InitStructure.SSP_FrameFormat = SSP_FRAME_FORMAT_SPI;
	SSP_InitStructure.SSP_Mode = SSP_MODE_MASTER;
	SSP_InitStructure.SSP_WordLength = SSP_WORD_LENGTH_8BIT;
	SSP_InitStructure.SSP_LoopbackMode = DISABLE;
	SSP_InitStructure.SSP_FastSPISlave = DISABLE;

	SSP_InitStructure.SSP_ClockPolarity = SSP_CLOCK_POLARITY_LOW;
	SSP_InitStructure.SSP_ClockPhase    = SSP_CLOCK_PHASE_1EDGE;

	SSP_Init(LCD_SSP, &SSP_InitStructure);
	SSP_Cmd(LCD_SSP,ENABLE);
}

void drv_display_write(uint8_t data, bool command)
{
    if(!command)
    	PORT_SetBits(LCD_PORT, LCD_A0);
    else
    	PORT_ResetBits(LCD_PORT, LCD_A0);

    PORT_ResetBits(LCD_PORT, LCD_CS);
    while(!SSP_GetFlagStatus(LCD_SSP,SSP_FLAG_TNF));
    SSP_SendData(LCD_SSP,data);
    while(SSP_GetFlagStatus(LCD_SSP,SSP_FLAG_BSY));
    PORT_SetBits(LCD_PORT, LCD_CS);
}

void drv_display_writeArray(uint8_t *data, uint8_t count, bool command)
{
    if(!command)
    	PORT_SetBits(LCD_PORT, LCD_A0);
    else
    	PORT_ResetBits(LCD_PORT, LCD_A0);

    PORT_ResetBits(LCD_PORT, LCD_CS);
    for(int i = 0;i<count; i++)
    {
    	while(!SSP_GetFlagStatus(LCD_SSP,SSP_FLAG_TNF));
    	SSP_SendData(LCD_SSP,data[i]);
    }
    while(SSP_GetFlagStatus(LCD_SSP,SSP_FLAG_BSY));
    PORT_SetBits(LCD_PORT, LCD_CS);
}

void drv_display_initLcd(void)
{
	PORT_ResetBits(LCD_PORT, LCD_RES);
	PORT_SetBits(LCD_PORT, LCD_CS);
	for(int q=0;q<100000;q++){}
	PORT_SetBits(LCD_PORT, LCD_RES);
	for(int q=0;q<100000;q++){}
	uint8_t arrayDisplayInit[15] = {0x2F,
			0x27,//0x20-0x27 LDO regulator //0x20-0v | 0x27-12v
			0x89,0x00,
			0x81,0x30,//81 command 3F
			0x40,
			0xA6,//A6 normal, A7 reverse
			0xA4,//A4 normal, A5 all points on
			0xC8,0xA0,0xAF};
	drv_display_writeArray(arrayDisplayInit, 13, true);
}

void VoltageToStr(char* buff, int voltage)
{
	if(voltage<0) voltage = 0;
	if(voltage<10000)
	{
		xsprintf(buff, "%d.%02d В",voltage/1000,voltage%1000/10);
		return;
	}
	if(voltage<100000)
	{
		xsprintf(buff, "%d.%d В",voltage/1000,voltage%1000/100);
		return;
	}
	xsprintf(buff, "%d В",voltage/1000);
}

void CurrentToStr(char* buff, int current)
{
	if(current<1000)
	{
		xsprintf(buff, "%d мА",current);
		return;
	}
	if(current<10000)
	{
		xsprintf(buff, "%d.%02d А",current/1000,current%1000/100);
		return;
	}
	xsprintf(buff, "%d А",current/1000);
}

void PowerToStr(char* buff, int power)
{
	xsprintf(buff, "%3d.%02d",power/100,power%100);
}

void drv_display_drawCapture(void)
{
	GF_Line(0,0,0,63);
	GF_Line(127,0,127,63);
	GF_Line(0,0,127,0);
	GF_Line(0,63,127,63);
	GF_Line(0,10,127,10);
	char c = drv_getStatusTampers() ? 'L': 'O';
	GF_Printf(1,2, "%02d:%02d:%02d %02d.%02d.%04d %c",
			drv_clock_getHour(),drv_clock_getMinutes(),drv_clock_getSeconds(),
			drv_clock_getDay(),drv_clock_getMonth(),drv_clock_getYear(),c);
}

void drv_display_drawPhaseDisplay(void)
{
	char buff[32];
	GF_Clear();
	drv_display_drawCapture();
	GF_Line(0,40,127,40);
	GF_Line(42,10,42,63);
	GF_Line(84,10,84,63);

	GF_Printf(2,12,"ФАЗА A");
	VoltageToStr(buff,(int)drv_adcui_getVoltage(adcuiChannelA));
	GF_Printf(2,22,buff);
	CurrentToStr(buff,(int)drv_adcui_getCurrent(adcuiChannelA));
	GF_Printf(2,32,buff);
	GF_Printf(2,42,"Вт*ч");
	PowerToStr(buff,(int)drv_adcui_getEnergy(adcuiChannelA));
	GF_Printf(2,52,buff);

	GF_Printf(44,12,"ФАЗА B");
	VoltageToStr(buff,(int)drv_adcui_getVoltage(adcuiChannelB));
	GF_Printf(44,22,buff);
	CurrentToStr(buff,(int)drv_adcui_getCurrent(adcuiChannelB));
	GF_Printf(44,32,buff);
	GF_Printf(44,42,"Вт*ч");
	PowerToStr(buff,(int)drv_adcui_getEnergy(adcuiChannelB));
	GF_Printf(44,52,buff);

	GF_Printf(86,12,"ФАЗА C");
	VoltageToStr(buff,(int)drv_adcui_getVoltage(adcuiChannelC));
	GF_Printf(86,22,buff);
	CurrentToStr(buff,(int)drv_adcui_getCurrent(adcuiChannelC));
	GF_Printf(86,32,buff);
	GF_Printf(86,42,"Вт*ч");
	PowerToStr(buff,(int)drv_adcui_getEnergy(adcuiChannelC));
	GF_Printf(86,52,buff);

}

void drv_display_drawPhaseDisplayX(ADCUIChannel channel)
{
	GF_Clear();
	drv_display_drawCapture();
	ADCUIChannelDataStruct data;
	drv_adcui_getChannelData(channel, &data);
	switch(channel)
	{
		case adcuiChannelA: GF_DrawStr(2,12,"ФАЗА A"); break;
		case adcuiChannelB: GF_DrawStr(2,12,"ФАЗА B"); break;
		case adcuiChannelC: GF_DrawStr(2,12,"ФАЗА C"); break;
	}
	GF_Printf(61,12+8*0, "P=%ld Вт*ч",data.activePWH + data.activeNWH + data.reactivePWH + data.reactiveNWH);
	GF_Line(0,20,127,20);
	GF_Line(38,20,38,63);
	GF_Line(64,20,64,63);
	GF_Line(96,20,96,63);

	GF_Printf(2, 22,"Общие");
	GF_Printf(40,22,"Акт.");
	GF_Printf(66,22,"Реак.");

	GF_Line(0,30,127,30);

	GF_Printf(98,16+8*2,"+Вт");
	GF_Printf(98,16+8*3,"-Вт");
	GF_Printf(98,16+8*4,"+Вт*ч");
	GF_Printf(98,16+8*5,"-Вт*ч");

	if(data.voltage<0) data.voltage = 0;
	GF_Printf(2,16+8*2, "%4ldВ",data.voltage/1000);
	GF_Printf(2,16+8*3, "%2ld.%ldА",data.current/1000,data.current/100%10);
	GF_Printf(2,16+8*4, "%4ldВт",data.power/1000000);

	//active
	GF_Printf(40,16+8*2, "%4ld",data.activeP/10000);
	GF_Printf(40,16+8*3, "%4ld",data.activeN/10000);
	GF_Printf(40,16+8*4, "%4ld",data.activePWH/1);
	GF_Printf(40,16+8*5, "%4ld",data.activeNWH/1);

	//reacive
	GF_Printf(66,16+8*2, "%4ld",data.reactiveP/10000);
	GF_Printf(66,16+8*3, "%4ld",data.reactiveN/10000);
	GF_Printf(66,16+8*4, "%4ld",data.reactivePWH/1);
	GF_Printf(66,16+8*5, "%4ld",data.reactiveNWH/1);
}

void drv_display_drawTamperDisplay(void)
{

	GF_Clear();
	drv_display_drawCapture();
	GF_Line(0,28,127,28);
	GF_Line(0,46,127,46);

	for(uint8_t i = 0; i < 3; i++)
	{
		int yoffset = i*18;
		GF_Printf(7,yoffset+12, "Tamper %d   %2d:%02d:%02d",
				i+1,drv_clock_getTamperHour(i),drv_clock_getTamperMinutes(i),
				drv_clock_getTamperSeconds(i));
		if(drv_getStatusTamperNumber(i))
			GF_Printf(7,yoffset+20,"Unlock");
		else
			GF_Printf(7,yoffset+20,"Lock");

		GF_Printf(61,yoffset+20, "%2d.%02d.%04d",drv_clock_getTamperDay(i),
				drv_clock_getTamperMonth(i),drv_clock_getTamperYear(i));
	}
}

void drv_display_drawClockDisplay(enumDisplayClockSetPage page)
{
	GF_Clear();
	drv_display_drawCapture();
	if(page == 0)
	{
		GF_Printf(2,12, "Clock calib.    0x%02X",drv_clock_getCalibValue());
		GF_Printf(2,20, "Temp compensation %2d",drv_clock_getTempRes());
		GF_Printf(10,29,"25");
		GF_Printf(10 ,52," 0");
		uint8_t xoffset = 22;
		for(uint8_t i = 0; i <16; i++)
		{
			uint8_t value = drv_clock_getTempConst(i);
			if(value<24)
				GF_DrawSymbolGraph(xoffset+i*5,32,(uint8_t)(0xFF<<(24-value)));
			else
				GF_DrawSymbolGraph(xoffset+i*5,32,0xFF);
			if(value<16)
				GF_DrawSymbolGraph(xoffset+i*5,40,(uint8_t)(0xFF<<(16-value)));
			else
				GF_DrawSymbolGraph(xoffset+i*5,40,0xFF);
			if(value<8)
				GF_DrawSymbolGraph(xoffset+i*5,48,(uint8_t)(0xFF<<(8 -value)));
			else
				GF_DrawSymbolGraph(xoffset+i*5,48,0xFF);
		}
		GF_DrawSymbol(xoffset+drv_clock_getTempRes()*5,56,'|');
	}
	else
	{
		page--;
		GF_Printf(2,12, "Clock set");
		GF_Printf(2,22, "Date %02d %02d %04d",drv_clock_getDay(),drv_clock_getMonth(),drv_clock_getYear());
		GF_Printf(2,33, "Time %02d %02d %02d",drv_clock_getHour(),drv_clock_getMinutes(),drv_clock_getSeconds());
		int x = 32+((int)page%3)*18;
		int y = 20+((int)page/3)*11;
		int x2 = page == 2? x+24: x+11;
		GF_RectLine(x-1,y,x2,y+11);

	}
}

void drv_display_drawPowerDisplay(void)
{
	char buff[32];
	GF_Clear();
	drv_display_drawCapture();
	GF_Printf(2, 12, "5V:   %d.%02d",drv_adc_getVoltage5V()/100,drv_adc_getVoltage5V()%100);
	GF_Printf(2, 20, "Bat:  %d.%02d",drv_adc_getBatteryVoltage()/100,drv_adc_getBatteryVoltage()%100);
	GF_Printf(2, 28, "BatI: %d.%02d",drv_adc_getBatteryVoltageInternal()/100,drv_adc_getBatteryVoltageInternal()%100);
	GF_Printf(2, 36, "Vref: %d.%02d",drv_adc_getVref()/100,drv_adc_getVref()%100);
	int tempAbs = drv_adc_getTemp()>0 ? drv_adc_getTemp():-drv_adc_getTemp();
	GF_Printf(2, 44, "Temp: %2d.%01d",drv_adc_getTemp()/10,tempAbs%10);

	GF_Printf(2, 52, "Null curr.:");
	int cuurentNull = (int32_t)drv_adcui_getCurrentNull();
	CurrentToStr(buff,cuurentNull);
	GF_DrawStr(67, 52, buff);
}

void drv_display_drawFlashDisplay(void)
{
	GF_Clear();
	drv_display_drawCapture();
	GF_Printf(2,12,"Flash id 0x%X",drv_flash_getId());
	GF_Printf(2,20,"ADCUI save NONE");
	GF_Printf(2,28,"TIME  save NONE");
	GF_Printf(2,36,"last save  %d",0);
	GF_Printf(2,42,"count save %d",0);
}

void drv_display_init(void)
{
	drv_display_initPins();
	drv_display_initSSP();
	drv_display_initLcd();
	GF_SetBuffer(bufferDisplay);
	drv_display_drawPhaseDisplay();
	drv_display_update();
}

void drv_display_update()
{
	static uint64_t timeLastDisplayDraw = 0;
	if(getMillis() - timeLastDisplayDraw < 200) return;
	timeLastDisplayDraw=getMillis();
	switch(currentDisplay)
	{
	case DisplayPageMain	: drv_display_drawPhaseDisplay(); break;
	case DisplayPagePhaseA	: drv_display_drawPhaseDisplayX(adcuiChannelA); break;
	case DisplayPagePhaseB	: drv_display_drawPhaseDisplayX(adcuiChannelB); break;
	case DisplayPagePhaseC	: drv_display_drawPhaseDisplayX(adcuiChannelC); break;
	case DisplayPageTamper	: drv_display_drawTamperDisplay(); break;
	case DisplayPageClock	: drv_display_drawClockDisplay(currentSubDisplay); break;
	case DisplayPagePower	: drv_display_drawPowerDisplay(); break;
	case DisplayPageCalibrate : drv_display_drawCalibrateDisplay(currentSubDisplay); break;
	//case DisplayPageFlash	: drv_display_drawFlashDisplay(); break;
	}

	uint8_t *p = bufferDisplay;
	uint8_t arrayDisplayDrawCommand[3] = {0xB0,0x10,0x00};
	for (int y = 0; y < 8; y++)
	{
		drv_display_writeArray(arrayDisplayDrawCommand,3,true);
		drv_display_writeArray(p,128,false);
		arrayDisplayDrawCommand[0]++;
		p+=128;
	}
}

void drv_display_setCurrentDisplay(enumDisplayPage page)
{
	currentDisplay = page;
}

void drv_display_setNextDisplay(void)
{
	if(currentSubDisplay != 0)
	{
		if(currentDisplay == DisplayPageClock)
		{
			int seconds = drv_clock_getSeconds();
			int minutes = drv_clock_getMinutes();
			int hour = drv_clock_getHour();
			int day = drv_clock_getDay();
			int month = drv_clock_getMonth();
			int year = drv_clock_getYear();
			switch(currentSubDisplay)
			{
			case DisplayPageClockSetMain	: break;
			case DisplayPageClockSetDay		: day= day%31 + 1;	 break;
			case DisplayPageClockSetMonth	: month= month%12+1;	 break;
			case DisplayPageClockSetYear	: year= ++year%40;		 break;
			case DisplayPageClockSetHour	: hour= ++hour%24;		 break;
			case DisplayPageClockSetMinutes	: minutes= ++minutes%60; break;
			case DisplayPageClockSetSeconds	: seconds= 0; 			 break;
			}
			if(currentSubDisplay>DisplayPageClockSetYear)
				drv_clock_setTime(hour, minutes, seconds);
			else
				drv_clock_setDate(day, month, year);

		}
		return;
	}
	currentDisplay++;
	currentDisplay%=9;
}

void drv_display_setNextSubDisplay(void)
{
	drv_display_ledOn();
	currentSubDisplay++;
	currentSubDisplay%=subPageCount[currentDisplay];
}

uint8_t drv_display_getDisplaySubPage(void)
{
	return currentSubDisplay;
}

void drv_display_ledOn(void)
{
	timeLastDisplayLed = getMillis();
	PORT_SetBits(LCD_PORT, LCD_LED);
}

void drv_display_ledOff(void)
{
	PORT_ResetBits(LCD_PORT, LCD_LED);
}
