#include "Timer_lite.h"

//** CONSTRUCTOR **
Timer_lite::Timer_lite(uint8_t relay_pin)
{
	setRelayPin(relay_pin);
	_init();
}

Timer_lite::Timer_lite(uint8_t relay_pin, uint8_t initial_eeprom_address)
{
	setRelayPin(relay_pin);
	_init(initial_eeprom_address);
}

//** DESTRUCTOR **
Timer_lite::~Timer_lite()
{
}


//** MÉTODOS PRIVADOS**
// Para constructor
	void Timer_lite::_init(uint8_t initial_eeprom_address = 0)
	{
		_instancia_num = ++_instancias_num;

        _eeprom_address_tiempo_inicio_espera 	= initial_eeprom_address;
        _eeprom_address_tiempo_arranque 	 	= initial_eeprom_address +4;
        _eeprom_address_tiempo_arranque_espera 	= initial_eeprom_address +8;
        _eeprom_address_tiempo_encendido 		= initial_eeprom_address +12;
        _eeprom_address_tiempo_encendido_espera = initial_eeprom_address +16;
        _eeprom_address_timer_detenido 			= initial_eeprom_address +20;
	}


// EEPROM
	void Timer_lite::EEPROMWritelong(uint8_t address, uint32_t value)
	{
		//Decomposition from a long to 4 bytes by using bitshift.
		//One = Most significant -> Four = Least significant byte
		uint8_t four  = (value & 0xFF);
		uint8_t three = ((value >> 8) & 0xFF);
		uint8_t two   = ((value >> 16) & 0xFF);
		uint8_t one   = ((value >> 24) & 0xFF);

		//Write the 4 bytes into the eeprom memory.
		EEPROM.write(address, four);
		EEPROM.write(address + 1, three);
		EEPROM.write(address + 2, two);
		EEPROM.write(address + 3, one);
	}

	uint32_t Timer_lite::EEPROMReadlong(uint8_t address)
	{
		//Read the 4 bytes from the eeprom memory.
		uint32_t four  = EEPROM.read(address);
		uint32_t three = EEPROM.read(address + 1);
		uint32_t two   = EEPROM.read(address + 2);
		uint32_t one   = EEPROM.read(address + 3);
		
		//Return the recomposed long by using bitshift.
		return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
	}


// Proximo periodo
/*
	void Timer_lite::proximoPeriodo()
	{
		switch (getPeriodo()){
			case TIMER_INICIO_ESPERA:
				_periodo      = TIMER_ARRANQUE;
				_periodo_time = getTiempoArranque();
				break;

			case TIMER_ARRANQUE:
				_periodo      = TIMER_ARRANQUE_ESPERA;
				_periodo_time = getTiempoArranqueEspera();
				break;

			case TIMER_ARRANQUE_ESPERA:
				_periodo      = TIMER_ENCENDIDO;
				_periodo_time = getTiempoEncendido();
				break;

			case TIMER_ENCENDIDO:
				_periodo      = TIMER_ENCENDIDO_ESPERA;
				_periodo_time = getTiempoEncendidoEspera();
				break;

			case TIMER_ENCENDIDO_ESPERA:
				_periodo      = TIMER_ENCENDIDO;
				_periodo_time = getTiempoEncendido();
				break;
		}

		// Si Retraso o Arranque igual a 0, recalculamos
		if((status_temporizador == TIMER_RETARDO || status_temporizador == TIMER_ARRANQUE) && periodo_time == 0)
			procesar_proximo_periodo();
	}
*/


//** MÉTODOS PUBLICOS **
// Set Fecha y Hora
	void Timer_lite::setFechaHoraSistema()
	{
		_RTC.adjust(DateTime(__DATE__, __TIME__));
	}

	void Timer_lite::setFecha(uint16_t anho, uint8_t mes, uint8_t dia)
	{
		_RTC.adjust(DateTime(anho, mes, dia, _now.hour(), _now.minute(), _now.second()));
	}

	void Timer_lite::setHora(uint8_t hrs, uint8_t min, uint8_t seg)
	{
		_RTC.adjust(DateTime(_now.year(), _now.month(), _now.day(), hrs, min, seg));
	}

	void Timer_lite::setFechaHora(uint16_t anho, uint8_t mes, uint8_t dia, uint8_t hrs, uint8_t min, uint8_t seg)
	{
		_RTC.adjust(DateTime(anho, mes, dia, hrs, min, seg));
	}


// Get Fecha y Hora
	String Timer_lite::getFecha()
	{
		String year  = (String) _now.year();
		String month = (_now.month() < 10? "0": "") + (String) _now.month();
		String day   = (_now.day() < 10? "0": "") + (String) _now.day();

		return day + "/" + month + '/' + year;
	}

	String Timer_lite::getHora()
	{
		String hour   = (_now.hour() < 10? "0": "") + (String) _now.hour();
		String minute = (_now.minute() < 10? "0": "") + (String) _now.minute();
		String second = (_now.second() < 10? "0": "") + (String) _now.second();

		return hour + ":" + minute + ':' + second;
	}


// Configuraciones Hardware
	void Timer_lite::setRelayPin(uint8_t relay_pin)
	{
		_relay_pin = relay_pin;
	}

	void Timer_lite::setRelaySatus(uint8_t relay_status)
	{
		_status_relay = relay_status;

		digitalWrite(_relay_pin, relay_status);
	}

	uint8_t Timer_lite::getStatusRelay()
	{
		return _status_relay;
	}


// Set Configuraciones Timer_lite
	void Timer_lite::setTiempoInicioEspera(uint32_t segundos)
	{
		EEPROMWritelong(_eeprom_address_tiempo_inicio_espera, segundos);
	}

	void Timer_lite::setTiempoInicioEspera(uint8_t minutos, uint8_t segundos)
	{
		uint32_t _segundos = (minutos * 60) + segundos;

		EEPROMWritelong(_eeprom_address_tiempo_inicio_espera, _segundos);
	}

	void Timer_lite::setTiempoArranque(uint32_t segundos)
	{
		EEPROMWritelong(_eeprom_address_tiempo_arranque, segundos);
	}

	void Timer_lite::setTiempoArranque(uint8_t horas, uint8_t minutos, uint8_t segundos)
	{
		uint32_t _segundos = (horas * 3600) + (minutos * 60) + segundos;

		EEPROMWritelong(_eeprom_address_tiempo_arranque, _segundos);
	}

	void Timer_lite::setTiempoArranqueEspera(uint32_t segundos)
	{
		EEPROMWritelong(_eeprom_address_tiempo_arranque_espera, segundos);
	}

	void Timer_lite::setTiempoArranqueEspera(uint8_t horas, uint8_t minutos, uint8_t segundos)
	{
		uint32_t _segundos = (horas * 3600) + (minutos * 60) + segundos;

		EEPROMWritelong(_eeprom_address_tiempo_arranque_espera, _segundos);
	}

	void Timer_lite::setTiempoEncendido(uint32_t segundos)
	{
		EEPROMWritelong(_eeprom_address_tiempo_encendido, segundos);
	}

	void Timer_lite::setTiempoEncendido(uint8_t horas, uint8_t minutos, uint8_t segundos)
	{
		uint32_t _segundos = (horas * 3600) + (minutos * 60) + segundos;

		EEPROMWritelong(_eeprom_address_tiempo_encendido, _segundos);
	}

	void Timer_lite::setTiempoEncendidoEspera(uint32_t segundos)
	{
		EEPROMWritelong(_eeprom_address_tiempo_encendido_espera, segundos);
	}

	void Timer_lite::setTiempoEncendidoEspera(uint8_t horas, uint8_t minutos, uint8_t segundos)
	{
		uint32_t _segundos = (horas * 3600) + (minutos * 60) + segundos;

		EEPROMWritelong(_eeprom_address_tiempo_encendido_espera, _segundos);
	}


// Get Timer_lite EEPROM
	uint32_t Timer_lite::getTiempoInicioEspera()
	{
		return EEPROMReadlong(_eeprom_address_tiempo_inicio_espera);
	}

	uint32_t Timer_lite::getTiempoArranque()
	{
		return EEPROMReadlong(_eeprom_address_tiempo_arranque);
	}

	uint32_t Timer_lite::getTiempoArranqueEspera()
	{
		return EEPROMReadlong(_eeprom_address_tiempo_arranque_espera);
	}

	uint32_t Timer_lite::getTiempoEncendido()
	{
		return EEPROMReadlong(_eeprom_address_tiempo_encendido);
	}

	uint32_t Timer_lite::getTiempoEncendidoEspera()
	{
		return EEPROMReadlong(_eeprom_address_tiempo_encendido_espera);
	}

	uint8_t Timer_lite::getTimerDetenido()
	{
		return EEPROM.read(_eeprom_address_timer_detenido);
	}


// Get Status Timer_lite
	uint8_t Timer_lite::getInstanciasNum()
	{
		return _instancias_num;
	}

	uint8_t Timer_lite::getInstanciaNum()
	{
		return _instancia_num;
	}




	uint8_t Timer_lite::getPeriodo()
	{
		return _periodo;
	}

	uint32_t Timer_lite::getFinPeriodoSeg()
	{
		return _fin_de_periodo_seg;
	}

	uint32_t Timer_lite::getRestantePeriodoSeg()
	{
		return _fin_de_periodo_seg - _now.unixtime();
	}





// PROCESO DEL TEMPORIZADOR
	void Timer_lite::initialize()
	{
		if(_instancia_num == 1){
			Wire.begin();

			if(!_RTC.begin())
				//Serial.println("Couldn't find RTC");
				serialPrintln("Couldn't find RTC");

			if(_RTC.lostPower()){
				//Serial.println("RTC lost power, lets set the time!");
				serialPrintln("RTC lost power, lets set the time!");

				//_RTC.adjust(DateTime(2021, 1, 1, 0, 0, 0));
			}

			_now = _RTC.now();
		}

  		pinMode(_relay_pin, OUTPUT);

		reiniciar();
	}

	void Timer_lite::process_loop()
	{
		if(_instancia_num == 1)
			_now = _RTC.now();

		if(getTimerDetenido()){
			setRelaySatus(RELAY_OFF);

		}else{
			if(_now.unixtime() >= _fin_de_periodo_seg){
				switch(_periodo){
					case TIMER_INICIO_ESPERA:
						_periodo            = TIMER_ARRANQUE;
						_fin_de_periodo_seg = _now.unixtime() + getTiempoArranque();

						setRelaySatus(RELAY_ON);
						break;

					case TIMER_ARRANQUE:
						_periodo            = TIMER_ARRANQUE_ESPERA;
						_fin_de_periodo_seg = _now.unixtime() + getTiempoArranqueEspera();

						setRelaySatus(RELAY_OFF);
						break;

					case TIMER_ENCENDIDO:
						_periodo            = TIMER_ENCENDIDO_ESPERA;
						_fin_de_periodo_seg = _now.unixtime() + getTiempoEncendidoEspera();

						setRelaySatus(RELAY_OFF);
						break;

					case TIMER_ARRANQUE_ESPERA:
					case TIMER_ENCENDIDO_ESPERA:
						_periodo            = TIMER_ENCENDIDO;
						_fin_de_periodo_seg = _now.unixtime() + getTiempoEncendido();

						setRelaySatus(RELAY_ON);
						break;
				}
			}

			/*
			// Una vez cada segundo por Instancia
			if(_now.unixtime() >= _T[2] +1) {
				writeMemory(_memory_address_periodo_time, --_periodo_time, 4);

				if(_periodo_time <= 0)
					proximoPeriodo();

				// Estatus del relevador
				_status_relay = (_periodo == TIMER_ARRANQUE || _periodo == TIMER_ENCENDIDO) && _periodo_time > 0;
				digitalWrite(_relay_pin, _status_relay);

				_T[2] = _now.unixtime();
			}
			*/
		}
	}


// Reinicio y detener del temporizador
	void Timer_lite::detener()
	{
		EEPROM.write(_eeprom_address_timer_detenido, 1);

		_restante_del_periodo_seg = _fin_de_periodo_seg - _now.unixtime();
	}

	void Timer_lite::reanudar()
	{
		EEPROM.write(_eeprom_address_timer_detenido, 0);

		_fin_de_periodo_seg = _now.unixtime() + _restante_del_periodo_seg;
	}

	void Timer_lite::reiniciar()
	{
        setRelaySatus(RELAY_OFF);

		_periodo            = TIMER_INICIO_ESPERA;
		_fin_de_periodo_seg = _now.unixtime() + getTiempoInicioEspera();
	}
