#include <user_config.h>
#include <SmingCore/SmingCore.h>
#include <Libraries/DHT/DHT.h>

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

#include "max7219.h"
#include "webserver.h"

DHT dht(DHT_PIN, DHT22);

Timer procTimer, procRTimer;
Timer displayTimer, tmpTimer;
Timer showHighTimer, showLowTimer;
Timer brightTimer;

// Sensors values
float SensorT, SensorH, SensorHI, SensorCR;
String StrCF;
// Time values
time_t Time, NTPLastUpdate;
DateTime dt;

void process(void);
void connectOk(void);
void connectFail(void);
void showWatch(void);
void showTime(void);
void showTemperature(void);
void showHumidity(void);
void setBright(void);

// NTP Client
void
onNtpReceive(NtpClient& client, time_t timestamp);
NtpClient ntpClient("ntps1-0.cs.tu-berlin.de", 300, onNtpReceive);

void init()
{
	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);

	WifiStation.waitConnection(connectOk, 20, connectFail); // We recommend 20+ seconds for connection timeout at start

	// DHT sensor start
	dht.begin();

	// опрос датчиков -- раз в минуту?
	procTimer.initializeMs(60000, process).start();
	process();

	// 7-segment output
	MAX7219_Init();
	tmpTimer.initializeMs(10, MAX7219_FillMinus).startOnce();
	brightTimer.initializeMs(1000, setBright).start();

	// обновление экрана два раза в секунду
	displayTimer.initializeMs(500, showWatch).start();
}

void showWatch()
{
	static uint8_t old_si = 0xFF;
	static time_t oldTime;

	Time = SystemClock.now();

	dt.setTime(Time);

	/*
	 * теперь в dt у нас следующее:
	 * 	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
	 */

	uint8_t si = dt.Second / 5;
	/*
	 * 0 - Temp
	 * 1 - Humidity
	 * 2 - Temp
	 * 3 - Humidity
	 * 4 - Temp
	 * 5 - Humidity
	 * 6 - Temp
	 * 7 - Humidity
	 * 8 - Temp
	 * 9 - Humidity
	 * 10 - Temp
	 * 11 - Humidity
	 */

	if (oldTime == Time)
	{
	// Старая секунда
		// time the same, output blank for [HH MM]
		MAX7219_writeData(MAX7219_DIGIT2, SYM_BLANK);
		/*
		if (si != 5 || si != 11)
		{
			MAX7219_writeData(MAX7219_DIGIT2, SYM_BLANK);
		}
		*/
	}
	else
	{
	// Новая секунда
		oldTime = Time;

		// Запланируем обновление индикаторов
		showHighTimer.initializeMs(10, showTime).startOnce();

		switch(dt.Second / 15)
		{
			case 0:
				MAX7219_writeData(MAX7219_DIGIT2, SYM_Minus);
				break;
			case 1:
				MAX7219_writeData(MAX7219_DIGIT2, SYM_Minus2L);
				break;
			case 2:
				MAX7219_writeData(MAX7219_DIGIT2, SYM_Minus2H);
				break;
			case 3:
				MAX7219_writeData(MAX7219_DIGIT2, SYM_Minus3);
				break;
		}

		if (old_si != si)
		{
			old_si = si;

			switch (si)
			{
			case 0:
				showLowTimer.initializeMs(20, showTemperature).startOnce();
				break;
			case 1:
				showLowTimer.initializeMs(20, showHumidity).startOnce();
				break;
			case 2:
				showLowTimer.initializeMs(20, showTemperature).startOnce();
				break;
			case 3:
				showLowTimer.initializeMs(20, showHumidity).startOnce();
				break;
			case 4:
				showLowTimer.initializeMs(20, showTemperature).startOnce();
				break;
			case 5:
				showLowTimer.initializeMs(20, showHumidity).startOnce();
				break;
			case 6:
				showLowTimer.initializeMs(20, showTemperature).startOnce();
				break;
			case 7:
				showLowTimer.initializeMs(20, showHumidity).startOnce();
				break;
			case 8:
				showLowTimer.initializeMs(20, showTemperature).startOnce();
				break;
			case 9:
				showLowTimer.initializeMs(20, showHumidity).startOnce();
				break;
			case 10:
				showLowTimer.initializeMs(20, showTemperature).startOnce();
				break;
			case 11:
				showLowTimer.initializeMs(20, showHumidity).startOnce();
				break;
			}
		}
	}
}

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

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

		MAX7219_writeData(MAX7219_DIGIT3, dt.Minute / 10);
		MAX7219_writeData(MAX7219_DIGIT4, dt.Minute % 10);

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

			// уберём ведущий ноль у часов
			if (dt.Hour < 10)
			{
				MAX7219_writeData(MAX7219_DIGIT0, MAX7219_CHAR_BLANK);
			}
			else
			{
				MAX7219_writeData(MAX7219_DIGIT0, dt.Hour / 10);
			}
			MAX7219_writeData(MAX7219_DIGIT1, dt.Hour % 10);

		}
	}

}

/*
 * Выводим температуру на нижние индикаторы
 */
void showTemperature(void)
{
	MAX7219_writeData(MAX7219_DIGIT5, SYM_Temp);
	MAX7219_writeData(MAX7219_DIGIT6, (int) (SensorT) / 10);
	MAX7219_writeData(MAX7219_DIGIT7, (int) (SensorT) % 10);
}

/*
 * Выводим влажность на нижние индикаторы
 */
void showHumidity(void)
{
	MAX7219_writeData(MAX7219_DIGIT5, SYM_H_SM);
	MAX7219_writeData(MAX7219_DIGIT6, (int) (SensorH) / 10);
	MAX7219_writeData(MAX7219_DIGIT7, (int) (SensorH) % 10);
}

/*
 * Выводим дату на верхние индикаторы [DD MM]
 */
/*
 void showDate(void)
 {
 MAX7219_writeData(MAX7219_DIGIT0, dt.Day/10);
 MAX7219_writeData(MAX7219_DIGIT1, dt.Day%10);
 MAX7219_writeData(MAX7219_DIGIT2, SYM_Minus);
 MAX7219_writeData(MAX7219_DIGIT3, dt.Month/10);
 MAX7219_writeData(MAX7219_DIGIT4, dt.Month%10);

 MAX7219_writeData(MAX7219_DIGIT6, (uint8_t)(dt.DayofWeek / 10));
 MAX7219_writeData(MAX7219_DIGIT7, (uint8_t)(dt.DayofWeek % 10));
 }
 */

/*
 * Автоматическая регулировка яркости индикаторов
 */
void setBright(void)
{
	// приведём к диапазону 0-16
	uint8_t bright = (1024 - system_adc_read()) >> 6;

	// переведём диапазон значений 9-16/1 в 1-15/2
	if (bright > 8)
	{
		bright = ( (bright - 9) * 2 ) + 1;
	}
	else
	{
		bright = 1;
	}

	MAX7219_writeData(MAX7219_MODE_INTENSITY, bright);
}

/*
 * Читаем данные с DHT22, в случае неудачи -- данные остануться старыми.
 * меня это полностью устраивает.
 */
void process()
{
	TempAndHumidity th;
	ComfortState cf;
	static int8_t status;

	if (dht.readTempAndHumidity(th))
	{
		status = 0;
		SensorT = th.temp;
		SensorH = th.humid;
		SensorHI = dht.getHeatIndex();
		SensorCR = dht.getComfortRatio(cf);

		switch (cf)
		{
		case Comfort_OK:
			StrCF = "OK";
			break;
		case Comfort_TooHot:
			StrCF = "Too Hot";
			break;
		case Comfort_TooCold:
			StrCF = "Too Cold";
			break;
		case Comfort_TooDry:
			StrCF = "Too Dry";
			break;
		case Comfort_TooHumid:
			StrCF = "Too Humid";
			break;
		case Comfort_HotAndHumid:
			StrCF = "Hot And Humid";
			break;
		case Comfort_HotAndDry:
			StrCF = "Hot And Dry";
			break;
		case Comfort_ColdAndHumid:
			StrCF = "Cold And Humid";
			break;
		case Comfort_ColdAndDry:
			StrCF = "Cold And Dry";
			break;
		default:
			StrCF = "Unknown";
			break;
		}
	}
	else
	{
		/*
		 * В случае, если от датчика ничего не получили, запустим повторный опрос через
		 * 10 секунд, но не более 5 раз подряд.
		 */
		if (status < 6)
		{
			status++;
			procRTimer.initializeMs(10000, process).startOnce();
		}
	}
}

void connectOk()
{
	WifiAccessPoint.enable(false);
	Serial.print("I'm connecteed. IP: ");
	Serial.println(WifiStation.getIP().toString());

	startWebServer();
}

/*
 * в случае неудачи подключения поднимаем точку доступа без авторизации
 */
void connectFail()
{
	WifiAccessPoint.config("MeteoConfig", "", AUTH_OPEN);
	WifiAccessPoint.enable(true);
// Stop main screen output
	procTimer.stop();
	displayTimer.stop();

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

	startWebServer();
	WifiStation.waitConnection(connectOk); // Wait connection
}

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