/*
 * InfoDisplay.cpp
 *
 *  Created on: Mar 3, 2025
 *      Author: Murdock
 */

#include "InfoDisplay.h"


const char STR_IATH_TITLE[] PROGMEM = "-Insid temp/hum-";
const char STR_OATH_TITLE[] PROGMEM = "-Outsd temp/hum-";
const char STR_GASSENS_TITLE[] PROGMEM = "-Gas/smk sensor-";
const char STR_VENT1_TITLE[] PROGMEM = "-Vent power out-";
const char STR_VENT2_TITLE[] PROGMEM = "-Vent Temp diff-";
const char STR_VENT3_TITLE[] PROGMEM = "-Vent FAN doors-";
const char STR_LIGHT_TITLE[] PROGMEM = "-LED strip ligh-";
const char STR_POWERREL_TITLE[] PROGMEM = "-PWR relay outp-";
const char STR_FIREDET_TITLE[] PROGMEM = "-Fire det (W/S)-";
const char STR_ARDMEM_TITLE[] PROGMEM = "- Memory usage -";
const char STR_AUTO_MODE_UNABLE_TSENS_E[] PROGMEM = "AUTO UNBL-T sens";

InfoDisplay::InfoDisplay(VentilationControl* ventControl, FireWatch* fireWatch, LCDHelper* lcdHelper, RotaryEncoder* encoder, unsigned long infoPageDisplayTime) :
		ventControl(ventControl), fireWatch(fireWatch), lcdHelper(lcdHelper), encoder(encoder), infoPageDisplayTime(infoPageDisplayTime), currInfoPage(INFO_PAGE_OATH),
		lastInfoPageSwitchTime(0), lastEncoderPos(0), settingsManager(nullptr), dhtSensors(nullptr), gasSensor(nullptr) {}

void InfoDisplay::init() {
	//store curr pos on init so we skip first false detection
	lastEncoderPos = encoder->getPosition();
	settingsManager = fireWatch->getSettingsManager();
	gasSensor = fireWatch->getGasSensor();
	dhtSensors = fireWatch->getDHTSensors();
}

/**
 * This function will display info pages, switch between them
 *
 */
void InfoDisplay::displayInfoPage() {
	unsigned long currTime = millis();
	//switch page if it is time

	//check if forced page switch by encoder
	encoder->tick(); // Update encoder state
	long currEncoderPos = encoder->getPosition();
	RotaryEncoder::Direction encoderDirection = encoder->getDirection();

	//if info page forced by rotary encoder...
	if (currEncoderPos != lastEncoderPos) {
		if (encoderDirection == RotaryEncoder::Direction::CLOCKWISE) {
			currInfoPage++;
		} else if (encoderDirection == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
      if (currInfoPage == 0) {
        currInfoPage = INFO_PAGE_MEM;
      } else {
			  currInfoPage--;
      }
		}
		lastEncoderPos = currEncoderPos;

		//make sure we do not cross bounds
		if (currInfoPage >= INFO_PAGE_COUNT) {
			currInfoPage = INFO_PAGE_IATH;
		}

		//reset time so the page does not switch unexpectedly on next call because of the time ran out
		lastInfoPageSwitchTime = currTime;
	} else {
		//if not, change normally on time basis
		if ((infoPageDisplayTime > 0) && ((currTime - lastInfoPageSwitchTime) > infoPageDisplayTime)) {
			lastInfoPageSwitchTime = currTime;
			currInfoPage++;
			//make sure we do not cross bounds
			if (currInfoPage >= INFO_PAGE_COUNT) {
				currInfoPage = INFO_PAGE_IATH;
			}
			//DEBUG_PRINTLN_F(F("Switching to info page: %d"), currInfoPage);
			//also log actual free memory on page switch
			//DEBUG_PRINTLN_F(F("Current free memory: %d"), Utils::getFreeMemory());
		}
	}

	switch (currInfoPage) {
	case INFO_PAGE_IATH:
		displayInfoPage_IATH();
		break;
	case INFO_PAGE_OATH:
		displayInfoPage_OATH();
		break;
	case INFO_PAGE_GASSENS:
		displayInfoPage_GasSens();
		break;
	case INFO_PAGE_VENT1:
		displayInfoPage_Vent1();
		break;
	case INFO_PAGE_VENT2:
		displayInfoPage_Vent2();
		break;
	case INFO_PAGE_VENT3:
		displayInfoPage_Vent3();
		break;
	case INFO_PAGE_LIGHT:
		displayInfoPage_Light();
		break;
	case INFO_PAGE_POWER_RELAY:
		displayInfoPage_PowerRelay();
		break;
	case INFO_PAGE_FIRE:
		displayInfoPage_Fire();
		break;
	case INFO_PAGE_MEM:
		displayInfoPage_Memory();
		break;
	default:
		lcdHelper->printTextToLCD(_loadFlashString(F("Matrix error!")), 0);
	}
}

/**
 * This function displays the inside air temperature and humidity info page
 *
 */
void InfoDisplay::displayInfoPage_IATH() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_IATH_TITLE)), 0);

	if (!dhtSensors->isInsideValid()) {
		lcdHelper->printTextToLCD(_loadFlashString(F("Insd sens error!")), 1);
	} else {
		float insideTemperature = dhtSensors->getInsideTemperature();
		if (insideTemperature > settingsManager->getWarningTemp()) {
			lcdHelper->displayFloatValue(STR_WARN, insideTemperature, STR_UNIT_CELSIUS, 1, 1);
		} else {
			lcdHelper->display2FloatValues(STR_TEMP, insideTemperature, STR_UNIT_CELSIUS, 1, STR_HUM, dhtSensors->getInsideHumidity(), STR_UNIT_PERCENT, 0, 1);
		}
	}
}

/**
 * This function displays the outside air temperature and humidity info page
 *
 */
void InfoDisplay::displayInfoPage_OATH() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_OATH_TITLE)), 0);

	if (!dhtSensors->isOutsideValid()) {
		lcdHelper->printTextToLCD(_loadFlashString(F("Outs sens error!")), 1);
	} else {
		lcdHelper->display2FloatValues(STR_TEMP, (float) dhtSensors->getOutsideTemperature(), STR_UNIT_CELSIUS, 1, STR_HUM, dhtSensors->getOutsideHumidity(), STR_UNIT_PERCENT, 0, 1);
	}
}

/**
 * This function displays the info about gas sensor readings
 *
 */
void InfoDisplay::displayInfoPage_GasSens() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_GASSENS_TITLE)), 0);

	if (gasSensor->isSensorCalibrated()) {
		word iCurrGasSensValue = gasSensor->getSensorValue();
		word iGasSensValPPM = gasSensor->getSensorValuePPM();
		// Format the temperature string
		int iGasSensvalPercent = map(iCurrGasSensValue, 0, 1023, 0, 100);

		if (iGasSensValPPM > settingsManager->getAlertPPM()) {
			lcdHelper->displayWordValue(STR_WARN, iGasSensValPPM, STR_UNIT_PPM, 1);
		} else {
			// print everything on the second row, the function will trim it if necessary
			lcdHelper->printFormattedTextToLCD(1, "C=%d %s (%d%%)", iGasSensValPPM, STR_UNIT_PPM, iGasSensvalPercent);
		}
	} else {
		// calculate how long we still have to wait for sensor warmup
		long waitTime = gasSensor->getSensorWarmupTime() - millis();
		if (waitTime < 0) {
			waitTime = 0;
		}

		// print everything on the second row, the function will trim it if necessary
		lcdHelper->printFormattedTextToLCD(1, "Warm-up: %d s", waitTime / MULTIPLIER_1000);
	}
}

/**
 * A function to display info on ventilation 1
 *
 */
void InfoDisplay::displayInfoPage_Vent1() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_VENT1_TITLE)), 0);

	if (settingsManager->getFireAlertMode() != FireAlertMode::FIRE_HAZARD) {
		if (settingsManager->getTempControlMode() == SettingsManager::AUTO_MODE) {
			if (dhtSensors->areValuesValid()) {
				word fanRPM = ventControl->calcFANRPM();
				lcdHelper->printFormattedTextToLCD(1, "Fan AT,RPM=%d", fanRPM);
			} else {
				//inside or outside temperature is invalid, auto mode will not work
				lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_AUTO_MODE_UNABLE_TSENS_E)), 1);
			}
		} else {
			if (ventControl->isFanOn()) {
				word fanRPM = ventControl->calcFANRPM();
				lcdHelper->printFormattedTextToLCD(1, "Fan ON,RPM=%d", fanRPM);
			} else {
				lcdHelper->printTextToLCD(_loadFlashString(F("Fan is OFF")), 1);
			}
		}
	} else {
		lcdHelper->printFormattedTextToLCD(1, "Fan OFF - %s", STR_SFA);
	}
}

/**
 * A function to display info on ventilation 2 - fan doors
 *
 */
void InfoDisplay::displayInfoPage_Vent2() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_VENT2_TITLE)), 0);

	//gte temp difference
	float fInsideTemp = dhtSensors->getInsideTemperature();
	float fMaxInsideTemp = ventControl->getMaxInsideTemp();
	float fTempDiff = fInsideTemp - fMaxInsideTemp;

	char valueStr1[10], valueStr2[10];
    dtostrf(fInsideTemp, 0, 1, valueStr1);
    dtostrf(fTempDiff, 0, 1, valueStr2);

    //displaying inside fInsideTemp/fMaxInsideTemp (fTempDiff)
    lcdHelper->printFormattedTextToLCD(1, "%s/%d (%s)", valueStr1, (byte)fMaxInsideTemp, valueStr2);
}


/**
 * A function to display info on ventilation 2 - fan doors
 *
 */
void InfoDisplay::displayInfoPage_Vent3() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_VENT3_TITLE)), 0);
	byte fanDoorAngle = settingsManager->getFanDoorAngle();

	if (settingsManager->getFireAlertMode() != FireAlertMode::FIRE_HAZARD) {
		if (fanDoorAngle == SettingsManager::FAN_DOOR_ANGLE_AUTO) {
			if (dhtSensors->areValuesValid()) {
				lcdHelper->printFormattedTextToLCD(1, STR_FORMAT_DOUBLE_STR, STR_ANGLE, STR_AUTO);
			} else {
				//inside or outside temperature is invalid, auto mode will not work
				lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_AUTO_MODE_UNABLE_TSENS_E)), 1);
			}
		} else {
			if (fanDoorAngle == 0) {
				lcdHelper->printFormattedTextToLCD(1, "%s", STR_CLOSED);
			} else {
				lcdHelper->displayByteValue(STR_ANGLE, fanDoorAngle, STR_UNIT_DEG, 1);
			}
		}
	} else {
		lcdHelper->printFormattedTextToLCD(1, "%s - %s", STR_CLOSED, STR_SFA);
	}
}

/**
 * A function to display info on lighting
 *
 */
void InfoDisplay::displayInfoPage_Light() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_LIGHT_TITLE)), 0);
	if (settingsManager->getLEDOn() == LIGHT_LED_ON) {
		lcdHelper->printFormattedTextToLCD(1, "%s, Brgt: %d%s", STR_ON, settingsManager->getLEDPWM(), STR_UNIT_PERCENT);
	} else {
		lcdHelper->printTextToLCD(STR_OFF, 1);
	}
}

/**
 * A function to display info on power relay
 *
 */
void InfoDisplay::displayInfoPage_PowerRelay() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_POWERREL_TITLE)), 0);
	if (settingsManager->getFireAlertMode() == FireAlertMode::FIRE_HAZARD) {
		lcdHelper->printFormattedTextToLCD(1, STR_FORMAT_PWR_RELAY_STATE, STR_STATE, STR_OFF, STR_SFA);
	} else {
		if (fireWatch->isPowerRelayOn()) {
			if (settingsManager->getPowerRelayOverride() != PWR_RELAY_OVERRIDE_ON) {
				lcdHelper->printFormattedTextToLCD(1, STR_FORMAT_DOUBLE_STR_SPACED, STR_STATE, STR_ON);
			} else {
				lcdHelper->printFormattedTextToLCD(1, STR_FORMAT_PWR_RELAY_STATE, STR_STATE, STR_ON, STR_OVERRIDE);
			}
		} else {
			lcdHelper->printFormattedTextToLCD(1, STR_FORMAT_DOUBLE_STR_SPACED, STR_STATE, STR_OFF);
		}
	}
}

/**
 * A function to display info on fire detection
 *
 */
void InfoDisplay::displayInfoPage_Fire() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_FIREDET_TITLE)), 0);
	lcdHelper->printFormattedTextToLCD(1, "%d/%d%s %s", settingsManager->getWarningTemp(), settingsManager->getShutdownTemp(), STR_UNIT_CELSIUS, (settingsManager->getFireAlertMode() == FireAlertMode::FIRE_HAZARD) ? STR_SFA : "");
}

void InfoDisplay::displayInfoPage_Memory() {
	lcdHelper->printTextToLCD(_loadFlashString(reinterpret_cast<const __FlashStringHelper*>(STR_ARDMEM_TITLE)), 0);
	lcdHelper->printFormattedTextToLCD(1, "%d|%d|%d",Utils::getTotalMemory(), Utils::getUsedMemory(), Utils::getFreeMemory());
}

unsigned long InfoDisplay::getInfoPageDisplayTime() const {
	return infoPageDisplayTime;
}

void InfoDisplay::setInfoPageDisplayTime(unsigned long displayTime) {
	infoPageDisplayTime = displayTime;
	lastInfoPageSwitchTime = millis();		//reset last change time
}
