#include "util.h"

unsigned char doSetup=0;
unsigned char lastSetup=0;
unsigned char setupIndex=0;
volatile unsigned char counter=0;
volatile unsigned char saveCounter=0;
volatile unsigned char setupCtr=SETUP_TIME;
struct backpackbutton button_up,button_down,button_mute,button_setup;
struct backpackbutton setup_p,setup_m,setup_next,setup_loadir;
unsigned char digits[5];
unsigned int xx=0;
int masterVol=0;    //tenths of a dB
int lastMasterVol=0;
int digipotVol=0;             //value sent to digipots
char updateFlag=0;
char muteState=0;
char lastMuteState=1;   //force ramp up conditions present at boot
char saveDone=1;
unsigned char slaveIN=0;
unsigned int colourSelect=0;
char codeSeen=0;    
char irTelltale=0;
//non volatile in EEPROM, backed by variables in RAM and saved by timer
__eeprom char storedMuteState=0;
__eeprom int storedVol=0;
//these are used straight from EEPROM
__eeprom eeprom_image_t cur={
    .d.IRdevice=0,
    .d.IRdownButton=224,
    .d.IRupButton=168,
    .d.IRmuteButton=194,
    .d.volMax=5,    //not too extreme
    .d.slaveInUse=1,
    .d.dbOffset=0,
    //.d.vol_0=VOL_0,
    //.d.vol_m=VOL_M,
    .d.chansInUse=16,
    .d.chanOffsets={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

const int setupMin[]={
    0,0,0,0,0,0,-20,1,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20
};

int setupMax[]={
    255,255,255,255,(MASTER_MAX/10),1,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
};

const char setupNames[][13]={
    "IR DEVICE # ",
    "IR DOWN CODE",
    "IR UP CODE  ",
    "IR MUTE CODE",
    "MAX VOLUME  ",
    "SLAVE IN USE",
    "LEVEL OFFSET",
    //"VOLUME NULL ",
    //"VOLUME SLOPE",
    "CHANS IN USE",    
    "CH  1 OFFSET",
    "CH  2 OFFSET",
    "CH  3 OFFSET",
    "CH  4 OFFSET",
    "CH  5 OFFSET",
    "CH  6 OFFSET",
    "CH  7 OFFSET",
    "CH  8 OFFSET",
    "CH  9 OFFSET",
    "CH 10 OFFSET",
    "CH 11 OFFSET",
    "CH 12 OFFSET",
    "CH 13 OFFSET",
    "CH 14 OFFSET",
    "CH 15 OFFSET",
    "CH 16 OFFSET",
    "CH 17 OFFSET",
    "CH 18 OFFSET",
    "CH 19 OFFSET",
    "CH 20 OFFSET"    
};

const int dpSteps[]={      //actual gain with default values
-431,-381,-349,-326,-307,-292,-279,-267,-257,-248,-239,-232,-225,-218,-212,-206,
-201,-195,-190,-186,-181,-177,-173,-169,-165,-161,-158,-154,-151,-148,-145,-142,
-139,-136,-133,-130,-128,-125,-122,-120,-117,-115,-113,-110,-108,-106,-104,-102,
 -99, -97, -95, -93, -91, -89, -88, -86, -84, -82, -80, -78, -77, -75, -73, -71,
 -70, -68, -66, -65, -63, -61, -60, -58, -57, -55, -54, -52, -51, -49, -48, -46,
 -45, -43, -42, -40, -39, -38, -36, -35, -34, -32, -31, -30, -28, -27, -26, -24,
 -23, -22, -20, -19, -18, -17, -15, -14, -13, -12, -10,  -9,  -8,  -7,  -5,  -4,
  -3,  -2,  -1,   1,   2,   3,   4,   5,   7,   8,   9,  10,  11,  12,  13,  15,
  16,  17,  18,  19,  20,  21,  23,  24,  25,  26,  27,  28,  29,  30,  32,  33,
  34,  35,  36,  37,  38,  39,  41,  42,  43,  44,  45,  46,  47,  48,  49,  51,
  52,  53,  54,  55,  56,  57,  58,  59,  61,  62,  63,  64,  65,  66,  67,  68,
  70,  71,  72,  73,  74,  75,  76,  78,  79,  80,  81,  82,  83,  85,  86,  87,
  88,  89,  90,  92,  93,  94,  95,  96,  98,  99, 100, 101, 103, 104, 105, 106,
 107, 109, 110, 111, 113, 114, 115, 116, 118, 119, 120, 122, 123, 124, 126, 127,
 129, 130, 131, 133, 134, 136, 137, 138, 140, 141, 143, 144, 146, 147, 149, 151,
 152, 154, 155, 157, 159, 160, 162, 164, 165, 167, 169, 171, 172, 174, 176, 178,
};

void __interrupt() isr(void){
    //IR first for timing priority
    if(iocxfbits.IRRX_PIN && PIE0bits.IOCIE){
        iocxfbits.IRRX_PIN=0;   //clear flag
        if(portbits.IRRX_PIN){IRstate=IRstate|2;}
        if(IRstate==2){      //positive edge/carrier off
            TMR1H=0;                 //reset counter, start of pulse            
            PIR1bits.TMR1IF=0;
        }else if(IRstate==1){    //negative edge/carrier on
            if(PIR1bits.TMR1IF){        //TMR1IF => overflow
                ir_t=IR_TIMEOUT;
                TMR1H=0;                 //reset counter, start of pulse            
                PIR1bits.TMR1IF=0;                
                irbufptr=0;              //start of packet/repeat
            }else{
                ir_t=TMR1H;              //count pulse length (periods of 32us)
                TMR1H=0;                 //reset counter, to avoid overflow          
            }
            irbuf[irbufptr]=ir_t;          
            if(irbufptr==2){rawIRpkt[0]=0;rawIRpkt[1]=0;rawIRpkt[2]=0;rawIRpkt[3]=0;IRerror=0;} //possible packet start
            if((ir_t>=IR_1_MIN)&&(ir_t<=IR_1_MAX)){
                rawIRpkt[(irbufptr-2)>>3]|=bitMasks[irbufptr];
            }else if((ir_t<IR_0_MIN)&&(ir_t>IR_0_MAX)){
                IRerror=1;
            }   
            if(irbufptr==1){                    //check for repeat
                if(irbuf[0]==IR_TIMEOUT){
                    if(irbuf[1]>=IR_RPT_MIN){
                        if(irbuf[1]<=IR_RPT_MAX){
                            IRrpt=1;
                        }
                    }
                }
            }else if(irbufptr==33){
                if(irbuf[0]==IR_TIMEOUT){   //possibly a valid packet
                    if((irbuf[1]>=IR_START_MIN)&&(irbuf[1]<=IR_START_MAX)){ //valid start
                        if(IRerror==0){
                            if((rawIRpkt[0]|rawIRpkt[1])==255){
                                if((rawIRpkt[2]|rawIRpkt[3])==255){
                                    IRadd=rawIRpkt[0];
                                    IRcode=rawIRpkt[2];
                                    IRready=1;
                                }
                            }
                        }
                    }
                }                
            }
            irbufptr++;
        }
        IRstate=(IRstate>>1)&3;
    }
    if(PIR2bits.TMR2IF && PIE2bits.TMR2IE){
        PIR2bits.TMR2IF=0;
        //counter++;        //not used
        if(saveCounter){saveCounter--;} //saturating countdown
        if(setupCtr){setupCtr--;}       //saturating countdown
        if(irTelltale){irTelltale--;}   //saturating countdown
    }   
}

void setDPchannels(int x,unsigned char n){    //set first n channels to x with respective offsets
    unsigned char i;
    int adjVol;
    for(i=0;i<n;i++){
        adjVol=cur.d.chanOffsets[i]+x;
        if(adjVol<0){adjVol=0;}
        if(adjVol>DP_MAX){adjVol=DP_MAX;} //maybe not this high?
        sendDP(i,(unsigned char)adjVol);
    }
}

void saveToEEPROM(void){
    if(storedMuteState!=muteState){
        storedMuteState=muteState;
    }
    if(storedVol!=masterVol){
        storedVol=masterVol;    
    }    
}

void flagSaveNeeded(void){
    saveDone=0;
    saveCounter=SAVE_TIME;    
}

void changeMute(char m){
    int i;
    if(m){      //ramp down
        for(i=digipotVol;i>0;i=i-MUTE_STEPS){
            setDPchannels(i,(unsigned char)cur.d.chansInUse);    //step                    
            delay(1);                       //soften ramp
        }                
        setDPchannels(0,(unsigned char)cur.d.chansInUse); //settle on finish
        latbits.SHDN_DP=0;      //turn off for hard mute
    }else{                              //ramp up
        setDPchannels(0,(unsigned char)cur.d.chansInUse);    //force to min
        latbits.SHDN_DP=1;              //release hard mute
        for(i=MUTE_STEPS;i<digipotVol;i=i+MUTE_STEPS){
            setDPchannels(i,(unsigned char)cur.d.chansInUse);    //step                    
            delay(1);                       //soften ramp
        }                
        setDPchannels(digipotVol,(unsigned char)cur.d.chansInUse); //settle on finish
    }    
}

void editSetup(int n){  //uses current setupIndex
    int t,t0;    
    t0=cur.e[setupIndex];   //get current
    t=t0;       
    t=t+n;                  //adjust copy
    if(t<setupMin[setupIndex]){t=setupMin[setupIndex];} //fix
    if(t>setupMax[setupIndex]){t=setupMax[setupIndex];}
    if(t0!=t){cur.e[setupIndex]=t;} //only change if needed
    if(cur.d.slaveInUse){
        if(cur.d.chansInUse>16){        //if there is an overlap, fix both
            cur.d.slaveInUse=0;
            cur.d.chansInUse=16;
        }
    }
    if(cur.d.slaveInUse){setupMax[7]=16;}else{setupMax[7]=20;}   //check shared resources
    if(cur.d.chansInUse>16){setupMax[5]=0;}else{setupMax[5]=1;}   //check shared resources    
}

void showN(int n,unsigned int x, unsigned int y){       //reusable code to print a number
    unsigned char s=' ';
    if(n<0){
        s='-';
        n=-n;
    }
    getDigits((unsigned int)n,digits); //abs returns an int!
    if(n<10){
        x=x+backpackcharfont(x,y,' ',WHITE,BLACK,arial_bold12x16);            
        x=x+backpackcharfont(x,y,s,WHITE,BLACK,arial_bold12x16);            
        backpackcharfont(x,y,digits[4]+'0',WHITE,BLACK,arial_bold12x16);            
    }else if(n<100){
        x=x+backpackcharfont(x,y,s,WHITE,BLACK,arial_bold12x16);            
        x=x+backpackcharfont(x,y,digits[3]+'0',WHITE,BLACK,arial_bold12x16);            
        backpackcharfont(x,y,digits[4]+'0',WHITE,BLACK,arial_bold12x16);            
    }else{  //this will give inaccurate values >999 or <-99
        x=x+backpackcharfont(x,y,digits[2]+'0',WHITE,BLACK,arial_bold12x16);            
        x=x+backpackcharfont(x,y,digits[3]+'0',WHITE,BLACK,arial_bold12x16);                
        backpackcharfont(x,y,digits[4]+'0',WHITE,BLACK,arial_bold12x16);            
    }
}

unsigned char sendSlave(unsigned char s){
    unsigned char r;
    SPI_BRG=TOUCH_BRG;  //use slower
    latbits.CS_5=0;
    SSP1BUF=s;
    while(SSP1STATbits.BF==0){}      //wait for SPI
    r=SSP1BUF;      //received byte
    latbits.CS_5=1;
    SPI_BRG=LCD_BRG;
    return r;
}

unsigned char getStep(int g){ //find step based on gain, ie inverse of dpSteps[]
    unsigned char s=128;    //start for binary search
    unsigned char i=64;     //step for binary search
    int r0,r1,r2;
    if(g<dpSteps[0]){return 0;}     //check outer bounds
    if(g>dpSteps[255]){return 255;}
    while(i){
        if(dpSteps[s]==g){return s;}    //bail on exact match
        if(g<dpSteps[s]){
            s=s-i;
        }else{
            s=s+i;
        }
        i=i>>1;
    }
    //check and validate. Since s is char over 8-bit array, we don't have to worry about bounds
    r0=abs(dpSteps[s-1]-g);
    r1=abs(dpSteps[s]-g);
    r2=abs(dpSteps[s+1]-g);
    //prefer lower for consistent biasing
    if(r0==0){return s-1;}      //exact match
    if(r1==0){return s;}        //exact match
    if(r2==0){return s+1;}      //exact match
    if((r0==r1)&&(r0<r2)){return s-1;}
    if((r2==r1)&&(r2<r1)){return s;}    
    if((r0<r1)&&(r0<r2)){return s-1;}
    if((r2<r0)&&(r2<r1)){return s+1;}
    return s;
}
