#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <avr/wdt.h>
#include <stdbool.h>

// Změna bitu portu, IO registru nebo proměnné:
#define SETB(port,pin)    port |= 1ul<<pin    //nastav bit
#define CLRB(port,pin)    port &= ~(1ul<<pin) //nuluj bit
#define NEGB(port,pin)    port ^= 1ul<<pin    //neguj bit

#define MASK_BIT(port,pin) (port&(1ul<<pin))



#define ERROR (1)
#define SUCCESS (0)

#define onMakro  (0)
#define offMakro (1)

#define LED_100PROC(onOffMakro);   		 if (onOffMakro==onMakro) { CLRB(PORTC,3); } else { SETB(PORTC,3); }
#define LED_90PROC(onOffMakro);    		 if (onOffMakro==onMakro) { CLRB(PORTC,2); } else { SETB(PORTC,2); }
#define LED_80PROC(onOffMakro);  		 if (onOffMakro==onMakro) { CLRB(PORTC,1); } else { SETB(PORTC,1); }
#define LED_70PROC(onOffMakro);  		 if (onOffMakro==onMakro) { CLRB(PORTC,0); } else { SETB(PORTC,0); }
#define LED_60PROC(onOffMakro);  		 if (onOffMakro==onMakro) { CLRB(PORTB,5); } else { SETB(PORTB,5); }
#define LED_50PROC(onOffMakro);  		 if (onOffMakro==onMakro) { CLRB(PORTB,4); } else { SETB(PORTB,4); }
#define LED_CHARGING(onOffMakro);  		 if (onOffMakro==onMakro) { CLRB(PORTB,3); } else { SETB(PORTB,3); }
#define LED_READY(onOffMakro); 			 if (onOffMakro==onMakro) { CLRB(PORTB,2); } else { SETB(PORTB,2); }
#define LED_LOWBATTERY(onOffMakro); 	 if (onOffMakro==onMakro) { CLRB(PORTB,1); } else { SETB(PORTB,1); }
#define LED_TRIGGERENABLED(onOffMakro);  if (onOffMakro==onMakro) { CLRB(PORTD,3); } else { SETB(PORTD,3); }

#define IS_SWITCH_ON_TRIGGERENABLE       (!(MASK_BIT(PINB,0)))

#define IS_BUTTON_ON_POWERSET            (!!(MASK_BIT(PIND,6)))
#define IS_BUTTON_AND_SWITCH_ON_CHARGE	 (!(MASK_BIT(PIND,7)))

#define OUT_MENIC(onOffMakro);         if (onOffMakro==onMakro) { CLRB(PORTD,5); } else { SETB(PORTD,5); }
#define OUT_ZAPALOVANI(onOffMakro);    if (onOffMakro==onMakro) { SETB(PORTD,4); } else { CLRB(PORTD,4); }

#define ADC_BAT  (4)
#define ADC_KOND (5)

#define LOW_BATTERY_THRESHOLD (796)
#define HYSTEREZE_ADC (10)

#define DOBA_ZAPALOVANI_MS (200)


typedef enum { eCekani, eNabijeni, eNabito, eNabitoPribijim }StavCinnosti;
typedef enum { e50 = 729, e60 = 799, e70 = 862, e80 = 922, e90 = 978, e100 = 1020 }HodnotaNabijeni;

//GLOBAL VARIABLES
volatile uint8_t btnCnt0 = 0;
volatile bool powerSetPressed = 0;
volatile bool isButtonPowerset = 0;
volatile StavCinnosti stavCinnosti = eCekani;
volatile uint16_t kamNabijet = 0;
volatile HodnotaNabijeni nastavenaHodnotaNabijeni = e50;
volatile bool isLowBattery = 0;
volatile bool forceToCekani = 0;


uint16_t zmerAD(uint8_t cisloKanalu);
uint16_t zmerADp(uint8_t cisloKanalu);
void posunHodnotuNabijeni();
void zobrazHodnotuNabijeni();


ISR(INT0_vect)
{
	if ((stavCinnosti==eNabito || stavCinnosti==eNabitoPribijim) && IS_SWITCH_ON_TRIGGERENABLE)
	{
		OUT_MENIC(offMakro);
		OUT_ZAPALOVANI(offMakro);
		_delay_us(100);
		OUT_ZAPALOVANI(onMakro);
		_delay_ms(DOBA_ZAPALOVANI_MS);
		OUT_ZAPALOVANI(offMakro);
		forceToCekani = 1;
	}
	
	
}

ISR(TIMER2_COMP_vect)
{
	if (IS_BUTTON_ON_POWERSET && !powerSetPressed)
	{
		isButtonPowerset=1;
		btnCnt0=3;
		powerSetPressed=1;
	}
	else if (IS_BUTTON_ON_POWERSET && btnCnt0 && powerSetPressed)
	{
		btnCnt0=3;
	}
	else if (!IS_BUTTON_ON_POWERSET && btnCnt0 && powerSetPressed)
	{
		--btnCnt0;
	}
	else if (!IS_BUTTON_ON_POWERSET && !btnCnt0 && powerSetPressed)
	{
		powerSetPressed=0;
	}
	


}

uint16_t zmerADp(uint8_t cisloKanalu)
{
	#define POCET_PRUMEROVANYCH_VZORKU (10)
	uint32_t prumer=0;
	for (int i=0; i<POCET_PRUMEROVANYCH_VZORKU; ++i)
	{
		prumer+=zmerAD(cisloKanalu);
	}
	prumer=prumer/POCET_PRUMEROVANYCH_VZORKU;
	
	return (prumer);
	
}


uint16_t zmerAD(uint8_t cisloKanalu)
{
	ADMUX = (ADMUX & 0xF0) + cisloKanalu;
	SETB(ADCSRA, ADSC);
	while (ADCSRA&(1 << ADSC)) {}
	/*SETB(ADCSRA, ADSC);
	while (ADCSRA&(1<<ADSC)) {}*/
	return (ADC);
}

void zobrazHodnotuNabijeni()
{
	switch (nastavenaHodnotaNabijeni)
	{
		case e50:
		LED_100PROC(offMakro);
		LED_90PROC(offMakro);
		LED_80PROC(offMakro);
		LED_70PROC(offMakro);
		LED_60PROC(offMakro);
		LED_50PROC(onMakro);
		break;

		case e60:
		LED_100PROC(offMakro);
		LED_90PROC(offMakro);
		LED_80PROC(offMakro);
		LED_70PROC(offMakro);
		LED_60PROC(onMakro);
		LED_50PROC(offMakro);
		break;

		case e70:
		LED_100PROC(offMakro);
		LED_90PROC(offMakro);
		LED_80PROC(offMakro);
		LED_70PROC(onMakro);
		LED_60PROC(offMakro);
		LED_50PROC(offMakro);
		break;

		case e80:
		LED_100PROC(offMakro);
		LED_90PROC(offMakro);
		LED_80PROC(onMakro);
		LED_70PROC(offMakro);
		LED_60PROC(offMakro);
		LED_50PROC(offMakro);
		break;

		case e90:
		LED_100PROC(offMakro);
		LED_90PROC(onMakro);
		LED_80PROC(offMakro);
		LED_70PROC(offMakro);
		LED_60PROC(offMakro);
		LED_50PROC(offMakro);
		break;

		case e100:
		LED_100PROC(onMakro);
		LED_90PROC(offMakro);
		LED_80PROC(offMakro);
		LED_70PROC(offMakro);
		LED_60PROC(offMakro);
		LED_50PROC(offMakro);
		break;

		default:
		nastavenaHodnotaNabijeni = e50;
		LED_100PROC(offMakro);
		LED_90PROC(offMakro);
		LED_80PROC(offMakro);
		LED_70PROC(offMakro);
		LED_60PROC(offMakro);
		LED_50PROC(onMakro);
		break;
	}



	return;
}

void posunHodnotuNabijeni()
{
	switch (nastavenaHodnotaNabijeni)
	{
		case e50:
		nastavenaHodnotaNabijeni = e60;
		break;

		case e60:
		nastavenaHodnotaNabijeni = e70;
		break;

		case e70:
		nastavenaHodnotaNabijeni = e80;
		break;

		case e80:
		nastavenaHodnotaNabijeni = e90;
		break;

		case e90:
		nastavenaHodnotaNabijeni = e100;
		break;

		case e100:
		nastavenaHodnotaNabijeni = e50;
		break;

		default:
		nastavenaHodnotaNabijeni = e50;
		break;
	}


	return;
}

int main(void)
{
	cli();

	DDRD = 0xFF;
	PORTD = 0xFF;

	DDRC = 0xFF;
	PORTC = 0xFF;

	DDRB = 0xFF;
	PORTB = 0xFF;
	
	//piny nastaveny jako vstup
	CLRB(DDRD, 2);//spousteni
	CLRB(DDRB, 0);//trig on
	CLRB(DDRD, 6);//power set
	CLRB(DDRD, 7);//charge button + switch
	
	OUT_MENIC(offMakro);
	OUT_ZAPALOVANI(offMakro);
	
	//externí přerušení
	SETB(MCUCR, ISC01);//INT0 falling edge
	SETB(GICR, INT0);//povolení přeruešní od external interruptu 0
	
	
	//časovač 2 - cca 8 Hz ale při 8 MHz, asi
	TCNT2 = 0;
	OCR2 = 121;
	TIMSK = (1 << OCIE2);//povolení přerušení procesoru od časovače 2
	TCCR2 = (1 << CS22) | (1 << CS21) | (1 << CS20) | (1 << WGM21);

	//AD převodník
	ADMUX = (1 << REFS0)|(1 << REFS1);
	ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0); //Prescale div factor = 32
	//merici piny nastavit jako vstupni a vypnout pull-up rezistory
	CLRB(DDRC, 5);
	CLRB(DDRC, 4);
	CLRB(PORTC, 5);
	CLRB(PORTC, 4);

	//watchdog
	/*MCUSR = 0;
	wdt_disable();
	wdt_reset();
	wdt_enable(WDTO_2S);
	*/
	//wdt_reset();


	LED_LOWBATTERY(onMakro);
	LED_100PROC(onMakro);
	LED_90PROC(onMakro);
	LED_80PROC(onMakro);
	LED_70PROC(onMakro);
	LED_60PROC(onMakro);
	LED_50PROC(onMakro);
	LED_CHARGING(onMakro);
	LED_READY(onMakro);
	LED_TRIGGERENABLED(onMakro);
	_delay_ms(500);
	zobrazHodnotuNabijeni();
	LED_CHARGING(offMakro);
	LED_READY(offMakro);
	LED_LOWBATTERY(offMakro);
	LED_TRIGGERENABLED(offMakro);
	
	
	sei();



	while (1)
	{
		if (zmerADp(ADC_BAT)<LOW_BATTERY_THRESHOLD)
		{
			isLowBattery = 1;
		}
		if (isLowBattery)
		{
			LED_LOWBATTERY(onMakro);
		}
		
		
		switch (stavCinnosti)
		{
		case eCekani:
			OUT_MENIC(offMakro);
			OUT_ZAPALOVANI(offMakro);
			if (isLowBattery)
			{
				LED_LOWBATTERY(onMakro);

				LED_100PROC(offMakro);
				LED_90PROC(offMakro);
				LED_80PROC(offMakro);
				LED_70PROC(offMakro);
				LED_60PROC(offMakro);
				LED_50PROC(offMakro);
				LED_CHARGING(offMakro);
				LED_READY(offMakro);
				LED_TRIGGERENABLED(offMakro);
			}
			else
			{
				LED_CHARGING(offMakro);
				LED_READY(offMakro);
				LED_TRIGGERENABLED(offMakro);
				if (isButtonPowerset)
				{
					isButtonPowerset = 0;
					posunHodnotuNabijeni();
					zobrazHodnotuNabijeni();
				}
				if (IS_BUTTON_AND_SWITCH_ON_CHARGE && !isLowBattery)
				{
					stavCinnosti = eNabijeni;
					kamNabijet = nastavenaHodnotaNabijeni;
				}
			}
			break;
		case eNabijeni:
			isButtonPowerset = 0;
			OUT_MENIC(onMakro);
			OUT_ZAPALOVANI(offMakro);
			LED_CHARGING(onMakro);
			LED_READY(offMakro);
			LED_TRIGGERENABLED(offMakro);
			if (zmerADp(ADC_KOND) >= kamNabijet)
			{
				stavCinnosti = eNabito;
				OUT_MENIC(offMakro);
				OUT_ZAPALOVANI(offMakro);
			}
			break;

		case eNabito:
			OUT_MENIC(offMakro);
			OUT_ZAPALOVANI(offMakro);
			LED_CHARGING(offMakro);
			LED_READY(onMakro);
			if (IS_SWITCH_ON_TRIGGERENABLE)
			{
				LED_TRIGGERENABLED(onMakro);
			}
			else
			{
				LED_TRIGGERENABLED(offMakro);
			}
			if (isButtonPowerset)
			{
				isButtonPowerset = 0;
				posunHodnotuNabijeni();
				zobrazHodnotuNabijeni();
			}
			if (zmerADp(ADC_KOND) <= (kamNabijet-HYSTEREZE_ADC))
			{
				stavCinnosti = eNabitoPribijim;
			}
			break;
		case eNabitoPribijim:
			OUT_MENIC(onMakro);
			OUT_ZAPALOVANI(offMakro);
			LED_CHARGING(onMakro);
			LED_READY(onMakro);
			if (IS_SWITCH_ON_TRIGGERENABLE)
			{
				LED_TRIGGERENABLED(onMakro);
			}
			else
			{
				LED_TRIGGERENABLED(offMakro);
			}
			if (isButtonPowerset)
			{
				isButtonPowerset = 0;
				posunHodnotuNabijeni();
				zobrazHodnotuNabijeni();
			}
			if (zmerADp(ADC_KOND) >= kamNabijet)
			{
				stavCinnosti = eNabito;
				OUT_MENIC(offMakro);
				OUT_ZAPALOVANI(offMakro);
			}
			break;
		default:
			stavCinnosti = eCekani;
			break;
		}
		
		if (forceToCekani)
		{
			forceToCekani = 0;
			stavCinnosti=eCekani;
		}


	}
	return SUCCESS;
}

