// speed = lower the slowest
// brightness = lower the brighter

//#define REDOUT PORTA.F5
//#define BLUEOUT PORTC.F5
//#define GREENOUT PORTA.F4
//the above is hard coded using ASM

#define MODE_BUTTON PORTC.F2
#define SUBMODE_BUTTON PORTC.F3
#define BRIGHTNESS_BUTTON PORTC.F1
#define SPEED_BUTTON PORTC.F4


unsigned char periodcount;
unsigned char red;
unsigned char green;
unsigned char blue;
unsigned int redbuffer;
unsigned int greenbuffer;
unsigned int bluebuffer;
unsigned char mode;
unsigned char submode[5];
unsigned char brightness;
unsigned char speed;
unsigned char button_debounce_delay_counter;
unsigned char button_debounce_delay_pre_counter;
unsigned char rotate_color_select;
unsigned int rotate_delay_counter;
unsigned char rotate_delay_pre_counter;
unsigned char step;
unsigned char step_count;
unsigned char brightness_revolve_offset;


unsigned const char Revolve_Sequence_Over_Values[16] = {0, 12, 8, 8, 8, 6, 4, 4, 4};

//'cycle' RGB
unsigned const char Revolve_Sequence_1[13] = {0, 0b00000001, 0b00000000, 0b00100000, 0b00000000, 0b00000100, 0b00000000, 0b00000010, 0b00000000, 0b00010000, 0b00000000, 0b00001000, 0b00000000};
//'cycle' GB
unsigned const char Revolve_Sequence_2[9] = {0, 0b00010000, 0b00000000, 0b00001000, 0b00000000, 0b00000100, 0b00000000, 0b00100000, 0b00000000};
//'cycle' RB
unsigned const char Revolve_Sequence_3[9] = {0, 0b00000001, 0b00000000, 0b00100000, 0b00000000, 0b00010000, 0b00000000, 0b00000010, 0b00000000};
//'cycle' RG
unsigned const char Revolve_Sequence_4[9] = {0, 0b00000001, 0b00000000, 0b00001000, 0b00000000, 0b00000100, 0b00000000, 0b00000010, 0b00000000};

//'cross' RGB
unsigned const char Revolve_Sequence_5[7] = {0, 0b00100001, 0b00000000, 0b00000110, 0b00000000, 0b00011000, 0b00000000};
//'cross' GB
unsigned const char Revolve_Sequence_6[5] = {0, 0b00100100, 0b00000000, 0b00011000, 0b00000000};
//'cross' RB
unsigned const char Revolve_Sequence_7[5] = {0, 0b00100001, 0b00000000, 0b00010010, 0b00000000};
//'cross' RG
unsigned const char Revolve_Sequence_8[5] = {0, 0b00001001, 0b00000000, 0b00000110, 0b00000000};



void Interrupt() {

   button_debounce_delay_pre_counter++;

   if (button_debounce_delay_pre_counter == 255) button_debounce_delay_counter++;

   if (button_debounce_delay_counter == 255) button_debounce_delay_counter = 255;


   rotate_delay_pre_counter++;

   if (rotate_delay_pre_counter == 255) rotate_delay_counter++;

   if (rotate_delay_counter > 30000) rotate_delay_counter = 30000; //don't allow counter to reset to zero



   periodcount++;

   if (periodcount == 0) {

         //if (  red != 0) REDOUT = 1;
         //if (green != 0) GREENOUT = 1;
         //if ( blue != 0) BLUEOUT = 1;

      asm {

         MOVF        _red, 0
                     BTFSS        STATUS, Z
         BSF        PORTA, 5

         MOVF        _green, 0
                     BTFSS        STATUS, Z
         BSF        PORTC, 5

         MOVF        _blue, 0
                     BTFSS        STATUS, Z
         BSF        PORTA, 4

      }

   }

   else

   {

      //if (  red < periodcount) REDOUT = 0;
      //if (green < periodcount) GREENOUT = 0;
      //if ( blue < periodcount) BLUEOUT = 0;

      asm {

         MOVF        _periodcount, 0
                            SUBWF        _red, 0
                            BTFSS        STATUS, C
               BCF        PORTA, 5

         MOVF        _periodcount, 0
                     SUBWF        _green, 0
                     BTFSS        STATUS, C
               BCF        PORTC, 5

         MOVF        _periodcount, 0
                     SUBWF        _blue, 0
                     BTFSS        STATUS, C
               BCF        PORTA, 4

      }

   }

   TMR0 = 200;
   INTCON.T0IF = 0; //clear flag

}//end interrupt()



void Bootup() {

   OSCCON = 0b01110001;     //set INTOSC to 8MHz
   CMCON0 = 0b00000111;    //turn off comparators
   ADCON0 = 0b00000000;     //turn off ADC
   ANSEL =  0b00000000;     //makes PORTA digital I/O
   //ANSELH = 0b00000000;     //makes PORTB digital I/O
   INTCON = 0b10100000;      //enable global and TMR0 interrupts.
   OPTION_REG = 0b11001000;  //no prescaler for TMR0
   TRISA = 0b00000000;
   PORTA = 0b00000000;
   TRISC = 0b00011110;
   PORTC = 0b00000000;
   WPUA = 0b00000000; //disable internal weak pull-ups

} //end Bootup()


void Revolve_Setup() {

   step = 0;
   step_count = 0;
   
   if (brightness == 1) brightness_revolve_offset = 0;
   if (brightness == 2) brightness_revolve_offset = 64;
   if (brightness == 3) brightness_revolve_offset = 127;
   if (brightness == 4) brightness_revolve_offset = 192;

   if (submode[mode] == 1) {

      red = 0;
      green = 0;
      blue = 255 - brightness_revolve_offset;

   }

   if (submode[mode] == 2) {

      red = 0;
      green = 255 - brightness_revolve_offset;
      blue = 0;

   }

   } //end Revolve_Setup()


void Button_Control() {


   if (MODE_BUTTON == 0 && SUBMODE_BUTTON == 0) {

      button_debounce_delay_counter = 0;

      mode = 1;

      return;
   
   }


   if (MODE_BUTTON == 0) {

      if (button_debounce_delay_counter < 45) return;

      button_debounce_delay_counter = 0;

      mode++;

      if (mode > 4) mode = 1;

      if (mode == 4) Revolve_Setup();

   }

   if (SUBMODE_BUTTON == 0) {

      if (button_debounce_delay_counter < 45) return;

      button_debounce_delay_counter = 0;

      submode[mode] = submode[mode]++;

      if (mode == 1) { //OFF

         if (submode[mode] > 1) submode[mode] = 1;

      }

      if (mode == 2) { //SOLID

         if (submode[mode] > 7) submode[mode] = 1;

      }

      if (mode == 3) { //ROTATE

         if (submode[mode] > 4) submode[mode] = 1;

      }

      if (mode == 4) { //REVOLVE

         if (submode[mode] > 3) submode[mode] = 1;

         //setup revolve vaules
         Revolve_Setup();

      }

   } //if (SUBMODE_BUTTON == 0)
   

   if (BRIGHTNESS_BUTTON == 0) {

      if (button_debounce_delay_counter < 45) return;

      button_debounce_delay_counter = 0;

      brightness++;

      if (brightness > 4) brightness = 1;
      
      if (mode == 4) Revolve_Setup();

   }


   if (SPEED_BUTTON == 0) {

      if (button_debounce_delay_counter < 45) return;

      button_debounce_delay_counter = 0;

      speed++;

      if (speed > 4) speed = 1;

   }

} //end void Button_Control()


void Set_Brightness() {

    //set brightness
    if (brightness == 1) {

       red = redbuffer;
       green = greenbuffer;
       blue = bluebuffer;

    }

    if (brightness == 2) {

       red = redbuffer >> 2;
       green = greenbuffer >> 2;
       blue = bluebuffer >> 2;

    }

    if (brightness == 3) {

       red = redbuffer >> 4;
       green = greenbuffer >> 4;
       blue = bluebuffer >> 4;

    }

    if (brightness == 4) {

       red = redbuffer >> 6;
       green = greenbuffer >> 6;
       blue = bluebuffer >> 6;

    }

} //end void Set_Brightness (


void Revolve() {

   unsigned char operation_code;

   if (rotate_delay_counter < (1 * speed)) return;

   rotate_delay_counter = 0;

      if (submode[mode] == 1) operation_code = Revolve_Sequence_1[step];
      if (submode[mode] == 2) operation_code = Revolve_Sequence_2[step];
      if (submode[mode] == 3) operation_code = Revolve_Sequence_3[step];
      if (submode[mode] == 4) operation_code = Revolve_Sequence_4[step];
      if (submode[mode] == 5) operation_code = Revolve_Sequence_5[step];
      if (submode[mode] == 6) operation_code = Revolve_Sequence_6[step];
      if (submode[mode] == 7) operation_code = Revolve_Sequence_7[step];
      if (submode[mode] == 8) operation_code = Revolve_Sequence_8[step];

      if (operation_code.F0 == 1) red++;
      if (operation_code.F1 == 1) red--;
      if (operation_code.F2 == 1) green++;
      if (operation_code.F3 == 1) green--;
      if (operation_code.F4 == 1) blue++;
      if (operation_code.F5 == 1) blue--;

      step_count++;

      if(step_count == 255 - brightness_revolve_offset) {

         step++;
         step_count = 0;

         //check here to see if at the end of the revolve_sequence
         if (step > Revolve_Sequence_Over_Values[submode[mode]]) step = 1;

      }

}   //end Revolve()


void Color_Control() {

   unsigned char Rotate_Color_Select_Buffer;

   unsigned char preset_color_duty[8][3] = {
   {0,0,0},          //OFF
   {255, 0, 0},      //Red
   {0, 255, 0},      //Green
   {0, 0, 255},      //Blue
   {255, 255, 0},    //Yellow
   {0, 255, 255},    //Cyan
   {255, 0, 255},    //Magenta
   {255, 255, 255}}; //White

   if (mode == 1) { //OFF

      red = 0;
      green = 0;
      blue = 0;

   } //if (mode == 1) //OFF



   if (mode == 2) { //SOLID

      redbuffer = preset_color_duty[submode[mode]][0];
      greenbuffer = preset_color_duty[submode[mode]][1];
      bluebuffer = preset_color_duty[submode[mode]][2];

      Set_Brightness();

   } //if (mode == 2) //SOLID


   if (mode == 3) { //ROTATE

      if (rotate_delay_counter < (75 * speed)) return;

      rotate_delay_counter = 0;

      //choose new color unlike previous color
      Rotate_Color_Select_Buffer = rotate_color_select;

      while(Rotate_Color_Select_Buffer == rotate_color_select) {

         if (submode[mode] == 1) Rotate_Color_Select_Buffer = Rand() / 4681 + 1;  //Random 1 - 7
         if (submode[mode] == 2) Rotate_Color_Select_Buffer = Rand() / 5461 + 1;  //Random 1 - 6
         if (submode[mode] == 3) Rotate_Color_Select_Buffer = Rand() / 10922 + 1; //Random 1 - 3
         if (submode[mode] == 4) Rotate_Color_Select_Buffer = Rand() / 10922 + 4; //Random 4 - 6

      }

      rotate_color_select = Rotate_Color_Select_Buffer;

      //set new color
      redbuffer = preset_color_duty[rotate_color_select][0];
      greenbuffer = preset_color_duty[rotate_color_select][1];
      bluebuffer = preset_color_duty[rotate_color_select][2];

      Set_Brightness();

   } //if (mode == 3) //ROTATE


    if (mode == 4) { //REVOLVE

      Revolve();

    } //if (mode == 4) //REVOLVE


} //void Color_Control()


void Main() {

   Bootup();

   mode = 1;

   submode[1] = 1;
   submode[2] = 1;
   submode[3] = 1;
   submode[4] = 1;

   brightness = 1;
   
   speed = 1;
   
   red = 0;
   green = 0;
   blue = 0 ;


   while(1) { //main loop

      Button_Control();

      Color_Control();

   }//end while

}//end program