#include <ioavr.h>
#include <inavr.h>
#define bit(x) (1<<(x))

#define MUX_CURR (bit(REFS1)|bit(MUX0))
#define MUX_VCC (bit(MUX3)|bit(MUX2))

#define MAXBRIGHT 3
#define MAXPWM 255
#define MAXCHARGECOUNTER 219726//(4000000*3600/65536)

unsigned int vcc=0;
unsigned int current=0;
__no_init char bright;
char ledoff=0;
char blinkoff=0;
__no_init char lock;
__flash char currents[MAXBRIGHT+1]={6,15,37,90};
__flash unsigned int consumption[MAXBRIGHT+1]={32,61,133,307};  //in mA
__no_init unsigned int total_charge; //mA*Hr
__no_init unsigned long charge_counter;
 char key_timer=0;
 char signal_led_timer=0;
 char startup_timer=0;
 unsigned int blink_timer=0;

__monitor setbright(char br){
  current=currents[br];
  bright=br;
  blink_timer=0;
}

#pragma vector=ADC_vect
__interrupt void adc(void){
#define TAU 5
 static int g=0;
 static char j=0;
 static char jj=0;
 static unsigned int adcsum=0;
 char tmp=j&31;
 if (tmp==30) ADMUX=MUX_VCC; else if (tmp==1) ADMUX=MUX_CURR;
 if (tmp==2){
   adcsum+=ADC;
   if (++jj==16) { vcc=adcsum>>4; adcsum=0; jj=0;}
 }
 else if (tmp>=5){
   g+=current-ADC+(j&1);
  if (g>=(MAXPWM<<TAU)) g=MAXPWM<<TAU;
  if ((g<0)||ledoff||blinkoff) g=0;
  OCR1B=(g>>TAU);
 }
 j++;
}

void poweroff(void){
 __disable_interrupt();
 __delay_cycles(100000);
 MCUSR=0;
 ADCSRA=0;
 TCCR1=0;
 GTCCR=0;
 TCCR0B=0;
 PORTB&=~(bit(1)|bit(0));
 WDTCR=bit(WDCE)|bit(WDE);
 WDTCR=0;
 PCMSK=bit(3);
 GIMSK=bit(PCIE);
 __enable_interrupt();
 MCUCR=bit(SE)|bit(SM1);
 __sleep();
 WDTCR=bit(WDCE)|bit(WDE);
 while(1);
}

#pragma vector=TIM0_OVF_vect
__interrupt void t0over(void){
  static char blink=0;
  if (signal_led_timer<255) signal_led_timer++;
  if (startup_timer<255) startup_timer++;
  if (key_timer<255) key_timer++;
  if ((OCR1B!=MAXPWM)&&!blink) blink_timer=0;
  blink_timer++;

  if (blink_timer==130) blink=1;
  if ((blink_timer==1700)||(blink_timer==1)) blink=0;
  if (blink_timer==1800) blink_timer=0;
  if ((blink_timer==160)||(blink_timer==175)) blinkoff=1;
  if ((blink_timer==162)||(blink_timer==177)) blinkoff=0;
  if (OCR1B!=MAXPWM) {
   charge_counter+=consumption[bright];
   if (charge_counter>=MAXCHARGECOUNTER) {charge_counter-=MAXCHARGECOUNTER; total_charge++;}
  }
}

__task void main(void){
 char oldkey;
 char startkey;
 __watchdog_reset();
 if ((MCUSR&(bit(PORF)|bit(EXTRF)))||(bright>MAXBRIGHT)){bright=0; total_charge=0; charge_counter=0; lock=1; poweroff();}
 else if (MCUSR&bit(BORF)) {/*if (bright) setbright(bright-1); else */poweroff();}
  else setbright(bright);
 MCUSR=0;
 WDTCR=bit(WDCE)|bit(WDE)|4;  //0.25s
 CLKPR=bit(CLKPCE);
 CLKPR=1; // div 2 , 4MHz
 PLLCSR=bit(7)|bit(PLLE);  //7=LSM
 while(!(PLLCSR&bit(PLOCK)));
 PLLCSR|=bit(PCKE);
// if (lock) ledoff=1;
 DDRB=bit(4)|bit(1)|bit(0);
 PORTB=0;
 GTCCR=bit(PWM1B)|bit(COM1B0);
 OCR1C=MAXPWM;
 OCR1B=0;
 TCCR1=bit(CS10);
 DIDR0=bit(ADC1D);
 ADMUX=MUX_CURR;
 ADCSRA=bit(ADEN)|bit(ADATE)|bit(ADIE)|bit(ADPS2)|bit(ADPS0);
 ADCSRB=0;
 ADCSRA|=bit(ADSC);
 TCCR0A=0;
 TCCR0B=bit(CS02); // div 256, 61 Hz
 TIMSK=bit(TOIE0);
 __enable_interrupt();
 while(1){
  char key;
  key=!(PINB&bit(3));
  PORTB&=~(bit(1)|bit(0));
  if (signal_led_timer<180){
   if (vcc>325) PORTB|=bit(1); else
    if (total_charge<800) PORTB|=bit(0); else
     if (total_charge<1100) PORTB|=bit(0)|bit(1); else PORTB|=bit(1);
  }
  if (startup_timer==3) startkey=key;
  if (key!=oldkey) signal_led_timer=0;
  if (key==0) {
    if (!startkey) {
     if (key_timer>=60){ lock=key_timer>180; poweroff();} else
      if (key_timer>3){ if (bright) bright--; else bright=MAXBRIGHT; setbright(bright);}
    } else startkey=0;
    if (lock) if ((key_timer>180) &&(key_timer<220)) {lock=0;ledoff=0;} else poweroff();
    key_timer=0;
  }
  else {
   if (!startkey)if (key_timer==60) ledoff=1;
   if (lock&&startkey&&(key_timer==180)) ledoff=1;
  }
  oldkey=key;
  __watchdog_reset();
  __delay_cycles(10000);
 }
}
