#include <SmingCore.h>

///////////////////////////////////////////////////////////////////
// Set your SSID & Pass for initial configuration
#include "configuration.h" // application configuration
///////////////////////////////////////////////////////////////////

#include "webserver.h"
#include "tm1650.h"
#include "AHTxx.h"
#include "led_spi.h"
#include "gy49.h"

Timer procTimer, procRTimer;
Timer displayTimer, tmpTimer;
Timer showHighTimer, showLowTimer;
Timer brightTimer;
// Sensors values
ahtxx_t sensorData;
float SensorT, SensorH, SensorHI, SensorCR;
String StrCF;
uint32_t SensorLux;
// Time values
time_t Time, NTPLastUpdate;
DateTime dt;

void GetData(void);
void connectOk(const String& SSID, MacAddress bssid, uint8_t channel);
void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason);
void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway);

void showWatch(void);
void showTime(void);
void showTemperature(void);
void showHumidity(void);
void showError(void);
void setBright(void);

// NTP Client
void onNtpReceive(NtpClient& client, time_t timestamp);
NtpClient ntpClient("ntp.time.in.ua", 1500, onNtpReceive); // every 15 min

void init(void) {
	spiffs_mount(); // Mount file system, in order to work with files

	Serial.begin(SERIAL_BAUD_RATE);  // 115200 by default
	Serial.systemDebugOutput(false); // Debug output to serial
	Serial.println("Wall Segment Clock");

	ActiveConfig = loadConfig();

	// set timezone hourly difference to UTC
	SystemClock.setTimeZone(ActiveConfig.AddTZ);

	WifiStation.config(ActiveConfig.NetworkSSID, ActiveConfig.NetworkPassword);
	WifiStation.enable(true);
	WifiAccessPoint.enable(false);

	WifiEvents.onStationConnect(connectOk);
	WifiEvents.onStationDisconnect(connectFail);
	WifiEvents.onStationGotIP(gotIP);

	// initialize I2C
	Wire.pins(4, 5);
	Wire.begin();

	// BIG digits
	LED_Init();

	// Low LED output
	TM1650_Init();
	brightTimer.initializeMs(1000, setBright).start();

	// refresh big led
	displayTimer.initializeMs(500, showWatch).start();

	// init sensors
	AHTxx_Init();
	GY49_Init();

	// polling sensors - once per two seconds
	procTimer.initializeMs(2000, GetData).start();
}

void showWatch(void) {
	static time_t oldTime;

	Time = SystemClock.now();

	dt.setTime(Time);
	/*
	 * Now, in dt we have:
	 * 	int8_t Hour;
	 * 	int8_t Minute;
	 * 	int8_t Second;
	 * 	int16_t Milliseconds;
	 * 	int8_t Day;
	 * 	int8_t DayofWeek; -- Sunday is day 0
	 * 	int8_t Month; // Jan is month 0
	 * 	int16_t Year;  // Full Year numer
	 */

	if (oldTime == Time) {
		// Old Second
		LED_SemicolonOFF();
	}	else {
		// New Second
		oldTime = Time;
		LED_ShowBin(dt.Hour, dt.Minute);
//		LED_ShowBin(dt.Minute, dt.Second);
		LED_SemicolonOn();

		if (dt.Second == 0x00) {
			Serial.printf("Time: %02d:%02d:00\r\n", dt.Hour, dt.Minute);
		}
	}
}

/*
 * Выводим текущее время [HH MM] на верхние индикаторы
 */
void showTime(void) {
	static uint8_t oldHour = 0xFF, oldMinute = 0xFF;

	if (oldMinute != dt.Minute) {
		oldMinute = dt.Minute;

		// ...
		
		if (oldHour != dt.Hour) {
			oldHour = dt.Hour;

			// ...
		} // new hour
	} // new minute
}

/*
 * Show temperature, small indicators
 */
void showTemperature(void) {
	uint8_t a, b;
	a = sensorData.Temperature / 100;
	b = (sensorData.Temperature % 100) / 10;
	TM1650_Out(a, b, 0, 0);
	TM1650_Out3(Sym_o);
	TM1650_Out4(Sym_C);
}

/*
 * Show humidity, small indicators
 */
void showHumidity(void) {
	uint8_t a, b;
	a = sensorData.Humidity / 100;
	b = (sensorData.Humidity % 100) / 10;
	TM1650_Out(a, b, 0, 0);
	TM1650_Out3(Sym_Off);
	TM1650_Out4(Sym_H);
}

/*
 * Show error, small indicators
 */
void showError(void) {
	TM1650_DotRes(Dig_2);
	TM1650_Out1(Sym_E);
	TM1650_Out2(Sym_r);
	TM1650_Out3(Sym_r);
	TM1650_Out4(Sym_Off);
}

/*
 * Выводим дату на верхние индикаторы [DD MM]
 */
 void showDate(void) {
 		// ...
}

/*
 * Автоматическая регулировка яркости индикаторов
 * GY-49 (MAX44009)
 */
void setBright(void) {
		// ...
}

/**
 * @brief Get data from Temperature/Humidity Sensor.
 */
void GetData(void) {
	static bool st = false;

	AHTxx_GetData(&sensorData);
	if (sensorData.Error != St_OK) {
		Serial.println("Sensor: Data error!");
		return;
	}

	SensorT = (float)sensorData.Temperature / 10;
	SensorH = (float)sensorData.Humidity / 10;

	if (st) {
		st = !st;
		showTemperature();
	} else {
		st = !st;
		showHumidity();
	}

	Serial.printf("Humidity: %d.%d %%; Temperature: %d.%d *C\r\n", sensorData.Humidity/10, sensorData.Humidity%10, sensorData.Temperature/10, sensorData.Temperature%10);

	SensorLux = GY49_GetData();
}

void connectOk(const String& SSID, MacAddress bssid, uint8_t channel)
{
	debugf("connected");
	WifiAccessPoint.enable(false);
}

void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway)
{
	Serial.print("Got IP address: ");
	Serial.println(ip);
	// Restart main screen output
	procTimer.restart();
	displayTimer.restart();

	// start NTP Client there?

	startWebServer();
}

void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason)
{
	debugf("connection FAILED: %s", WifiEvents.getDisconnectReasonDesc(reason).c_str());
	WifiAccessPoint.config("ClockConfig", "", AUTH_OPEN);
	WifiAccessPoint.enable(true);
	// Stop main screen output
	procTimer.stop();
	displayTimer.stop();

	Serial.println("WiFi ClockConfig");
	Serial.println(WifiAccessPoint.getIP());

	startWebServer();
	WifiStation.disconnect();
	WifiStation.connect();
}

/**
 * @brief NTP Client
 */
void onNtpReceive(NtpClient& client, time_t timestamp)
{
	SystemClock.setTime(timestamp, eTZ_UTC);
	NTPLastUpdate = SystemClock.now();
	Serial.println("*** Time synchronized OK! ***"); // DEBUG
}
