#define F_CPU 9600000 
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
typedef unsigned char bool; 
#define true  (0 == 0)
#define false (0 != 0)
#define MAX_U10BIT 0b0000001111111111 
#define INTERVAL         3   
#define CUR_MINIMAL_DIFF 50  
#define RES_MINIMAL_DIFF 50  
#define FREQ_DIV_OFFSET  2   
#define FREQ_MAXIMAL_DIV 6   
EEMEM unsigned int  EEPROM_cur_edge;
EEMEM unsigned int  EEPROM_res_edge; 
EEMEM unsigned char EEPROM_frequency_dividor;
unsigned int cur_edge, res_edge; 
unsigned char frequency_dividor; 
unsigned char clk = 0; 
bool tp_reset = false; 
static void init_vars(void) {
  if(MCUSR & (1 << EXTRF)) { 
    tp_reset = true;
    MCUSR &= ~(1 << EXTRF); 
  }
}
static void init_pins(void) {
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB4); 
}
static void init_interrupts(void) {
  sleep_enable(); 
  WDTCR = (1 << WDCE) | (1 << WDE); 
  WDTCR = (1 << WDTIE) | WDTO_1S; 
  sei(); 
}
void init_settings(void) {
  cur_edge = eeprom_read_word(&EEPROM_cur_edge); 
  res_edge = eeprom_read_word(&EEPROM_res_edge); 
  frequency_dividor = eeprom_read_byte(&EEPROM_frequency_dividor); 
}
static void toggle_load(bool state) {
  if(state) {
    PORTB |= (1 << PB1);
  } else {
    PORTB &= ~(1 << PB1);
  }
}
static void blink_load(unsigned char count) {
  for(unsigned char i = 0; i < count; ++i) {
    _delay_ms(200);
    toggle_load(true);
    _delay_ms(200);
    toggle_load(false);
  }
}
static void stop(void) {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  while(true) sleep_cpu();
}
static void toggle_amp(bool state) {
  if(state) {
    PORTB |= (1 << PB4); 
    _delay_ms(250);      
  } else {
    PORTB &= ~(1 << PB4);
  }
}
static void toggle_lpf(bool state) {
  if(state) {
    DDRB |= (1 << PB3); 
  } else {
    DDRB &= ~(1 << PB3); 
  }
}
static void toggle_gen(bool state) {
  if(state) {
    TCCR0A |= (1 << COM0A0) | (1 << WGM01); 
#ifndef PROTEUS
    TCCR0B |= (1 << CS00); 
#else
    TCCR0B |= (1 << CS00) | (1 << CS02); 
#endif
    OCR0A = FREQ_DIV_OFFSET + frequency_dividor; 
  } else {
    TCCR0A = 0; 
  }
}
static void toggle_adc(bool state) {
  if(state) {
    DDRB &= ~(1 << PB2); 
    ADMUX = 0b01 | (1 << REFS0); 
    ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | 
             (1 << ADIE) |  
             (1 << ADEN);   
  } else {
    ADCSRA = 0; 
    DDRB |= (1 << PB2); 
    _delay_ms(50); 
  }
}
static unsigned int do_adc(void) {
  set_sleep_mode(SLEEP_MODE_ADC); 
  do {
    sleep_cpu(); 
  } while(ADCSRA & (1 << ADSC)); 
  return ADC;
}
static unsigned int get_current(void) {
  unsigned int cur;
  toggle_lpf(true); 
  _delay_ms(150);
  toggle_adc(true); 
  _delay_ms(50); 
  cur = do_adc(); 
  toggle_adc(false);
  toggle_lpf(false);
  return cur;
}
static unsigned int get_resistance(void) {
  unsigned int res;
  toggle_gen(true); 
  _delay_ms(150);
  toggle_adc(true); 
  _delay_ms(50); 
  toggle_gen(false); 
  res = do_adc(); 
  toggle_adc(false);
  return MAX_U10BIT - res; 
}
static bool is_current(void) {
  return (get_current() >= cur_edge);
}
static bool is_toggled_on(void) {
  return (get_resistance() <= res_edge);
}
static void do_main(void) {
  toggle_amp(true); 
  if(is_current()) {
    toggle_load(false); 
  } else {
    if(is_toggled_on()) {
      toggle_load(true); 
    } else {
      toggle_load(false); 
    }
  }
  toggle_amp(false); 
}
static bool first_on(void) {
  return (frequency_dividor == 0xff); 
}
static void calibrate(void) {
  unsigned int cur_off, cur_on, res_off, res_on, res_on_tmp, res_off_array[FREQ_MAXIMAL_DIV + 1], diff, max_diff, frequency_dividor_tmp;
  blink_load(5); 
  _delay_ms(7500); 
  toggle_amp(true); 
  cur_off = get_current(); 
  
  for(frequency_dividor = 0; frequency_dividor <= FREQ_MAXIMAL_DIV; ++frequency_dividor) {
    res_off_array[frequency_dividor] = get_resistance();
  }
  blink_load(2); 
  _delay_ms(7500); 
  cur_on = get_current(); 
  blink_load(3); 
  _delay_ms(7500); 
  
  res_off = MAX_U10BIT;
  res_on = MAX_U10BIT;
  frequency_dividor_tmp = 0;
  max_diff = 0;
  
  for(frequency_dividor = 0; frequency_dividor <= FREQ_MAXIMAL_DIV; ++frequency_dividor) {
    res_on_tmp = get_resistance();
    
    if(res_off_array[frequency_dividor] > res_on_tmp) {
      diff = res_off_array[frequency_dividor] - res_on_tmp;
      if(diff > max_diff) {
        res_off = res_off_array[frequency_dividor];
        res_on = res_on_tmp;
        frequency_dividor_tmp = frequency_dividor;
        max_diff = diff;
      }    
    }
  }
  frequency_dividor = frequency_dividor_tmp;
  toggle_amp(false); 
  
  if(cur_on > cur_off + CUR_MINIMAL_DIFF) { 
    cur_edge = cur_off + (cur_on - cur_off) / 2; 
 
    if(res_on + RES_MINIMAL_DIFF < res_off) {
      res_edge = res_off - (res_off - res_on) / 2; 
      
      eeprom_write_word(&EEPROM_cur_edge, cur_edge);
      eeprom_write_word(&EEPROM_res_edge, res_edge);
      eeprom_write_byte(&EEPROM_frequency_dividor, frequency_dividor);
      
      blink_load(1); 
    } else {
      blink_load(2); 
      if(first_on()) stop();
    }
  } else {
    blink_load(3); 
    if(first_on()) stop();
  }
}
ISR(WDT_vect) {
  WDTCR |= (1 << WDTIE); 
}
EMPTY_INTERRUPT(ADC_vect); 
int main(void)
{
  init_vars();
  init_pins();       
  init_interrupts(); 
  init_settings();
  if(tp_reset || first_on()) {
    calibrate(); 
  }
  
  while(true) {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_cpu(); 
    if(++clk >= INTERVAL) {
      do_main(); 
      clk = 0;
    }
  }
}