//#include "OneButton.h"               // https://github.com/mathertel/OneButton
#include <FastLED.h>

#define DATA_PIN    3                //set your leds datapin   change to 32 for m5 atom lite
#define ATOMLED_PIN 4                //set your leds datapin   change to 27 for m5 atom lite
#define BUTTON_PIN_INPUT 2           //button pin   change to 39 for m5 atom lite

//uncomment this before upload to Atom Lite
// #define DATA_PIN    32                //set your leds datapin   change to 32 for m5 atom lite
// #define ATOMLED_PIN 27                //set your leds datapin   change to 27 for m5 atom lite
// #define BUTTON_PIN_INPUT 39           //button pin   change to 39 for m5 atom lite

#define LED_TYPE    WS2812B          //leds type
#define COLOR_ORDER GRB              //color order of leds

#define MAX_POWER_MILLIAMPS 700      //write here your power in milliamps. default i set 800 mA for safet

#define NUM_COLS_PLANAR 20            // resolution of planar lookup table
#define NUM_ROWS_PLANAR 20            // resolution of planar lookup table
#define NUM_LEDS_PLANAR NUM_COLS_PLANAR*NUM_ROWS_PLANAR

#define NUM_COLS_CILINDR 45           // resolution of cilindrical lookup table
#define NUM_ROWS_CILINDR 11            // resolution of cinindrical lookup table
#define NUM_LEDS_CILINDR NUM_COLS_CILINDR*NUM_ROWS_CILINDR

#define NUM_LEDS 256            

#define SECONDS_PER_PALETTE 10

uint16_t lastSafeIndex = 256; 

CRGB leds [NUM_LEDS+1];
byte rain [(NUM_COLS_PLANAR+2)*(NUM_ROWS_PLANAR+2)];   

//OneButton button(BUTTON_PIN_INPUT, true);

unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
byte automode = 1;        //change to 0 if you dont want automode on start 
byte InitNeeded = 1;
byte brigtness = 255;

uint8_t gCurrentPatternNumber = 0;    // Index number of which pattern is current

CRGBPalette16 gCurrentPalette( CRGB::Black);
CRGBPalette16 gTargetPalette ( CRGB::Black); 

#include "tables.h"
#include "patterns.h"


void setup() {
  Serial.begin(115200);

  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);

  //uncomment this before upload sketch
  // .setCorrection( TypicalLEDStrip );
  // FastLED.setMaxPowerInVoltsAndMilliamps( 5, MAX_POWER_MILLIAMPS);   
  FastLED.setBrightness(brigtness);
  FastLED.clear();
  
  next_Random_Pallete (CRGB (random_two_shifts (), random_two_shifts (), random_two_shifts ()));

  //button.attachClick(oneClick);
  //button.attachDoubleClick(doubleClick);
  //button.attachDuringLongPress(longPress);
  //  button.setDebounceTicks(80);
  Serial.println("controls: from start automode is enable and patterns change in loop");  
  Serial.println("one button click change pattern to next and automode is OFF");  
  Serial.println("double click change bright in loop 0..255 with 8 steps. not affect to Automode");  
  Serial.println("long press activate Automode ON");  
  Serial.println();
  if (automode) Serial.println("automode On"); else Serial.println("automode Off");
}


void checkAutomodeOn () {
  static unsigned long delayAutomode = 40000;   //how long buttons not pressed
  
  if (automode) return;  //if automode already On we dont need to check it 

  currentMillis = millis();
  if ((currentMillis - previousMillis) >= delayAutomode)
  {
    Serial.println("AutomodeOn");
    previousMillis = currentMillis;
    automode = 255;
  }
}


static void doubleClick() {
  Serial.println("DoubleClicked! next bright");
  static byte brIndex = 7;
  static byte bright[] = {0,16,32,64,96,128,160,255}; //8 steps 
  brIndex = (brIndex+1)%8;
  brigtness = bright[brIndex];
  FastLED.setBrightness(brigtness);
  Serial.println(brigtness);
}


static void oneClick() {
  Serial.println("Clicked! Next pattern. automode OFF");

  gCurrentPatternNumber = (gCurrentPatternNumber + 1 + ARRAY_SIZE(gPatterns)) % ARRAY_SIZE(gPatterns); //next effect
  InitNeeded=1; //flag if init something need
  previousMillis = millis();
  automode = 0;
}


static void longPress() {
  Serial.println("Long press!");
  Serial.println("AutomodeOn");
  previousMillis = currentMillis;
  automode = 255;
}


void FadeOut (byte steps){
  for (int i=0; i<=steps; i++) {
    gPatterns[gCurrentPatternNumber]();
    byte fadeOut = lerp8by8 (brigtness, 0, 255*i/steps);
    FastLED.setBrightness(fadeOut);
    FastLED.show(); 
    delay(10);
  }
}


void FadeIn (byte steps){
  for (int i=steps+1; i--; i>=0) {
    gPatterns[gCurrentPatternNumber]();
    byte fadeOut = lerp8by8 (brigtness, 0, 255*i/steps);
    FastLED.setBrightness(fadeOut);
    FastLED.show(); 
    delay(10);
  }
}


uint32_t random_two_shifts() {  //found this in https://cdn.hackaday.io/files/10721458687264/bh-badge-animate.c 
  static uint32_t r_z = 362436069, r_w = 521288629;

	r_z = 36969 * (r_z & 65535) + (r_z >> 16);
  r_w = 18000 * (r_w & 65535) + (r_w >> 16);

  return (r_z << 16) + r_w;
}



// void next_Random_Pallete (CRGB mixColor) {  
//   // main idea is generate random colors pallete 
//   // and mix random colors with one another color (color theory)
//   // this makes harmonic looking pallete vs poor random palette

//   for (int i = 0; i < 16; i++) {
//     gTargetPalette[i] = CRGB(0, 0, 0);  
//   }

//   byte leightFill = random8(8,15); //keep two or more last indexes to total black for nice spiral look 

//   for( int i = 0; i < leightFill; i++) {
//     uint32_t randomColor32 = random_two_shifts();  //got 32 bit random number and split it to 3 random 8 bit below 

//     byte r = (randomColor32 + mixColor.r)>>1;         //mix two value and divide by 2
//     byte g = ((randomColor32>>8) + mixColor.g)>>1;    //same
//     byte b = ((randomColor32>>16) + mixColor.b)>>1;   //same
    
//     gTargetPalette[i] = CRGB (r, g, b);
//   }

// }

void next_Random_Pallete (CRGB mixColor) {  
   // main idea is generate random colors pallete 
   // and mix random colors with one another color (color theory)
   // this makes harmonic looking pallete vs poor random palette
  for( int i = 0; i < 4; i++) {   // fill first 12 colors with 4 colors in pallete
    uint32_t randomColor32 = random_two_shifts();  //got 32 bit random number and split it to 3 random 8 bit below 

    byte r = (randomColor32 + mixColor.r)>>1;         //mix two value and divide by 2
    byte g = ((randomColor32>>8) + mixColor.g)>>1;    //same
    byte b = ((randomColor32>>16) + mixColor.b)>>1;   //same
    byte index = i*3;
    gTargetPalette[index++] = CRGB (r, g, b);
    gTargetPalette[index++] = CRGB (r, g, b);
    gTargetPalette[index] = CRGB (r, g, b);
      
  }
  
  gTargetPalette[12] = 0;    // fill last 4 colors in pallete with black
  gTargetPalette[13] = 0;
  gTargetPalette[14] = 0;
  gTargetPalette[15] = 0;

}

void loop() {

 // button.tick();
  // checkAutomodeOn ();   //you can uncomment this if you want to automatic return to automode after some time if buttons dont press 

  EVERY_N_SECONDS( SECONDS_PER_PALETTE ) {   //random change palettes
    CRGB mixColor = CRGB (random_two_shifts (), random_two_shifts (), random_two_shifts ());
    next_Random_Pallete (mixColor);
  }

  EVERY_N_MILLISECONDS(40) {   //blend current palette to next palette
    nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 16);
  }

  EVERY_N_SECONDS( 15 ) {  // speed of change patterns periodically
    if (automode) {
      // FadeOut (150);        // fade out current effect
      gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE(gPatterns); //next effect
      InitNeeded=1; //flag if init something need
      // FadeIn (200);        // fade in current effect
    }
  }
   
  gPatterns[gCurrentPatternNumber](); //play current pattern
 
  FastLED.show();
  //  delay(4);        //some time fast call rapidly FastLED.show() on esp32 causes flicker, this delay() is easy way to fix this 
}