#include "FastLED.h"

// Matrix size
#define NUM_ROWS 16
#define NUM_COLS 16
#define WIDTH NUM_COLS
#define HEIGHT NUM_ROWS
#define NUM_LEDS NUM_ROWS * NUM_COLS
#define MATRIX_TYPE 1
// LEDs pin
#define DATA_PIN 3
// LED brightness
#define BRIGHTNESS 255
// Define the array of leds
CRGB leds[NUM_LEDS];


//PaletteTester

byte type;
byte pal;
#define WIDTH NUM_COLS
#define HEIGHT NUM_ROWS

void fillAll(CRGB color) {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = color;
  }
}
void drawPixelXY(uint8_t x, uint8_t y, CRGB color)
{
  if (x < 0 || x > (WIDTH - 1) || y < 0 || y > (HEIGHT - 1)) return;
  uint32_t thisPixel = XY((uint8_t)x, (uint8_t)y);
  leds[thisPixel] = color;
}

// The 16 bit version of our coordinates
static uint16_t x;
static uint16_t y;
static uint16_t z;

uint16_t speed = 20;                                        // speed is set dynamically once we've started up
uint16_t scale = 30;                                        // scale is set dynamically once we've started up

// This is the array that we keep our computed noise values in
#define MAX_DIMENSION (max(WIDTH, HEIGHT))
#if (WIDTH > HEIGHT)
uint8_t noise[WIDTH][WIDTH];
#else
uint8_t noise[HEIGHT][HEIGHT];
#endif

static const TProgmemRGBPalette16 StepkosColors_p FL_PROGMEM = {0x0000ff, 0x0f00f0, 0x1e00e1, 0x2d00d2, 0x3c00c3, 0x4b00b4, 0x5a00a5, 0x690096, 0x780087, 0x870078, 0x9600cd, 0xa50050, 0xb40041, 0xc30032, 0xd20023, 0xe10014};
static const TProgmemRGBPalette16 AutumnColors_p FL_PROGMEM = {0xbc2300, 0xc84416, 0xdc642c, 0xe69664, 0xfbb979, 0xca503d, 0x882c1c, 0x9a3631, 0xa9624e, 0xcc9762, 0xdcc0b5, 0xc1a29f, 0x826468, 0x4a3334, 0x231a1a, 0x161113};
static const TProgmemRGBPalette16 NeonColors_p FL_PROGMEM = {0x9694ac, 0x979b9b, 0x888b8c, 0x767680, 0x696f77, 0x6c736f, 0x4097b8, 0x00b1d0, 0x0f93ec, 0x3572ff, 0x4157ff, 0x6162ff, 0x686cff, 0x7473ff, 0x8689e5, 0x9e9dc6};
static const TProgmemRGBPalette16 EveningColors_p FL_PROGMEM = {0x1e0443, 0x6d0081, 0x8200ac, 0x8200ac, 0x8200ac, 0x8200ac, 0x8200ac, 0x8200ac, 0x7900a1, 0x820055, 0xc80000, 0xe57b00, 0xff9d5a, 0xc58b32, 0xd8d400, 0xffff00};
static const TProgmemRGBPalette16 WoodColors_p FL_PROGMEM = {0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000, 0xcdb1a5, 0x7a4000};
static const TProgmemRGBPalette16 ZeebraColors_p FL_PROGMEM = {CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black, CRGB::White, CRGB::Black, CRGB::Black, CRGB::Black};
static const TProgmemRGBPalette16 WaterfallColors_p FL_PROGMEM = {0x000000, 0x060707, 0x101110, 0x151717, 0x1C1D22, 0x242A28, 0x363B3A, 0x313634, 0x505552, 0x6B6C70, 0x98A4A1, 0xC1C2C1, 0xCACECF, 0xCDDEDD, 0xDEDFE0, 0xB2BAB9};
static const TProgmemRGBPalette16 MercuryColors_p FL_PROGMEM = {0xffffff, 0xeeeeee, 0xdddddd, 0xcccccc, 0xbbbbbb, 0xaaaaaa, 0x999999, 0x888888, 0x777777, 0x666666, 0x555555, 0x444444, 0x333333, 0x222222, 0x111111, 0x000000};
static const TProgmemRGBPalette16 StepkosFireColors_p FL_PROGMEM = {0x000000, 0x0e0070, 0x1d00ad, 0x2d00c8, 0x3c00c3, 0x4b00b4, 0x5a00a5, 0x690096, 0x780087, 0x870078, 0x9600cd, 0xa50050, 0xb40041, 0xc30032, 0xd20023, 0xe10014};
static const TProgmemRGBPalette16 LakeColors_p FL_PROGMEM = {0x611de9, 0x7d7cf0, 0xa784f9, 0x8cb1ed, 0x7fffd4, 0x5ad5ec, 0x3399ff, 0x66afff, 0x50ffc1, 0x61d1e5, 0x68ff8d, 0x98ff61, 0xccff47, 0x7eff00, 0x53ff00, 0x00ff00};
static const TProgmemRGBPalette16 OrangeColors_p FL_PROGMEM = {0xffff00, 0xfff100, 0xffe100, 0xffd100, 0xffc100, 0xffb100, 0xffa100, 0xff9100, 0xff8100, 0xff7100, 0xff6100, 0xff5100, 0xff4100, 0xff3100, 0xff2100, 0xff1100};
static const TProgmemRGBPalette16 AcidColors_p FL_PROGMEM = {0xffff00, 0xd2b328, 0xd25228, 0x711323, 0x6c000c, 0x5a0c00, 0x6d373a, 0xaa5a62, 0x604564, 0x313164, 0x332765, 0x3a2465, 0x4b1665, 0x4b0069, 0x31004c, 0x200046};
static const TProgmemRGBPalette16 Hol_LightsColors_p FL_PROGMEM = {0x00ff00, 0x00c040, 0x008080, 0x0040c0, 0x0000ff, 0x4000c0, 0x800080, 0xc00040, 0xff0000, 0xff4000, 0xff8000, 0xd6c000, 0xffff00, 0xc0ff00, 0x80ff00, 0x40ff00};
extern const TProgmemRGBPalette16 AuroraColors_p1 FL_PROGMEM ={0x000000,0x162c2c, 0x55abab, 0x00FFFF, 0x55ABAB, 0x2c5555,0x162c2c, 0x000000, 0x000000, 0x000000,0x000000, 0x000000,  0x000000, 0x000000, 0x000000, 0x00000};
extern const TProgmemRGBPalette16 AuroraColors_p FL_PROGMEM ={0x000000, 0x003333, 0x006666, 0x009999, 0x00cccc,0x00ffff, 0x33ffff, 0x66ffff, 0x99ffff,0xccffff, 0xffffff, 0xffccff, 0xff99ff, 0xff66ff, 0xff33ff, 0xff00ff};
const TProgmemRGBPalette16 *palette_arr[] = {
  &AuroraColors_p,
  &PartyColors_p,
  &OceanColors_p,
  &LavaColors_p,
  &HeatColors_p,
  &ZeebraColors_p,
  &WaterfallColors_p,
  &CloudColors_p,
  &ForestColors_p,
  &RainbowColors_p,
  &RainbowStripeColors_p,
  &StepkosColors_p,
  &AutumnColors_p,
  &NeonColors_p,
  &AcidColors_p,
  &MercuryColors_p,
  &EveningColors_p,
  &LakeColors_p,
  &OrangeColors_p,
  &WoodColors_p,
  &AuroraColors_p1,
  &Hol_LightsColors_p
};
const TProgmemRGBPalette16 *curPalette = palette_arr[0];
void setCurrentPalette(uint8_t palIdx) {
  curPalette = palette_arr[palIdx];
}

uint8_t hue;
CRGB color;

bool loadingFlag = true;
int rad[(HEIGHT + WIDTH) / 8];
byte posx[(HEIGHT + WIDTH) / 8], posy[(HEIGHT + WIDTH) / 8];


void drawCircle(int x0, int y0, int radius, const CRGB &color) {
  int a = radius, b = 0;
  int radiusError = 1 - a;

  if (radius == 0) {
    drawPixelXY(x0, y0, color);
    return;
  }
  while (a >= b)  {
    drawPixelXY(a + x0, b + y0, color);
    drawPixelXY(b + x0, a + y0, color);
    drawPixelXY(-a + x0, b + y0, color);
    drawPixelXY(-b + x0, a + y0, color);
    drawPixelXY(-a + x0, -b + y0, color);
    drawPixelXY(-b + x0, -a + y0, color);
    drawPixelXY(a + x0, -b + y0, color);
    drawPixelXY(b + x0, -a + y0, color);
    b++;
    if (radiusError < 0)
      radiusError += 2 * b + 1;
    else
    {
      a--;
      radiusError += 2 * (b - a + 1);
    }
  }
}

void Enoise() {
  if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
  }
static bool eff =1;
  EVERY_N_MILLISECONDS(25000) { eff^=1; } //speed of  effect change 

   uint32_t ms = millis();
   for (byte y = 0; y < NUM_ROWS; y++) {
        for (byte x = 0; x < NUM_COLS; x++) {
          uint16_t pixelHue = inoise16 ((uint32_t)x*5000, (uint32_t)y*5000+ms*10,ms*20);  
          uint8_t pixelHue8 = inoise8 (x*15, y*15+ms/8,ms/70);
      leds[XY(x, y)] = ColorFromPalette(*curPalette, pixelHue8+ms/20, 255, LINEARBLEND);}

        }
}

void drop() {

  if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
    for (int i = 0; i < ((HEIGHT + WIDTH) / 8) - 1; i++)
    {
      posx[i] = random(WIDTH);
      posy[i] = random(HEIGHT);
      rad[i] = random(-1, (HEIGHT + WIDTH) / 2);
    }
  }
  fillAll(ColorFromPalette(*curPalette, hue));
  hue++;
  for (uint8_t i = 0; i < ((HEIGHT + WIDTH) / 8) - 1; i++)
  {
    drawCircle(posx[i], posy[i], rad[i], ColorFromPalette(*curPalette, (256 / 16) * 8.5 - rad[i] * 5 + hue));
    drawCircle(posx[i], posy[i], rad[i] - 1, ColorFromPalette(*curPalette, (256 / 16) * 7.5 - rad[i] * 5 + hue));
    if (rad[i] >= (HEIGHT + WIDTH) / 2) {
      rad[i] = -1;
      posx[i] = random(WIDTH);
      posy[i] = random(HEIGHT);
    }
    else
      rad[i]++;

  }
}
void rainbowHorVertRoutine(bool isVertical)
{ if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
  } hue += 4;
  for (uint8_t i = 0U; i < (isVertical ? WIDTH : HEIGHT); i++)
  {
    for (uint8_t j = 0U; j < (isVertical ? HEIGHT : WIDTH); j++)
    {
      drawPixelXY((isVertical ? i : j), (isVertical ? j : i), ColorFromPalette(*curPalette, (uint8_t)(hue + i * 10)));
    }
  }
}

// Yaroslaw Turbin, 22.06.2020
// https://vk.com/ldirko
// https://pastebin.com/eKqe4zzA
void Fire() {
  if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
  }	
 uint8_t speedy = 1;	 
  uint8_t _scale = 120;
  uint32_t a = millis();	 
for (byte i = 0U; i < WIDTH; i++) {
for (byte j = 0U; j < HEIGHT; j++) {
        drawPixelXY((WIDTH - 1) - i, j, ColorFromPalette(*curPalette, qsub8(inoise8(i * _scale, j * _scale + a, a / speedy), abs8(j - (HEIGHT - 1)) * 255 / (HEIGHT - 1)), 255));
  }	  
}	
}
//Metaballs
//16x16 rgb led matrix demo
//Yaroslaw Turbin 02.09.2020
//https://vk.com/ldirko
//https://www.reddit.com/user/ldirko/
uint8_t bx[5];
uint8_t by[5];
void meta() {
  if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
  }
  for (uint8_t a = 0; a < 5; a++) {
    bx[a] = beatsin8(15 + a * 2, 0, NUM_COLS - 1, 0, a * 32);
    by[a] = beatsin8(18 + a * 2, 0, NUM_ROWS - 1, 0, a * 32);
  }

  for (int i = 0; i < NUM_COLS; i++)    {
    for (int j = 0; j < NUM_ROWS; j++) {

      byte sum =  dist(i, j, bx[0], by[0]);
      for (uint8_t a = 1; a < 5; a++) {
        sum =  qadd8(sum, dist(i, j, bx[a], by[a]));
      }
      leds[XY (i, j)] =  ColorFromPalette(*curPalette, sum + 220, BRIGHTNESS);
    }
  }

  blur2d(leds, NUM_COLS, NUM_ROWS, 32 );

}
// (c) kostyamat 05.02.2021
// idea from https://www.reddit.com/r/FastLED/comments/jyly1e/challenge_fastled_sketch_that_fits_entirely_in_a/
// Thanks to https://www.reddit.com/user/ldirko/ Yaroslaw Turbin aka ldirko	// особая благодарность https://www.reddit.com/user/ldirko/ Yaroslaw Turbin aka ldirko
unsigned long timer;
    float adjastHeight;
    uint16_t adjScale;
    uint16_t _scale;
void aurora() {
  if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
    adjastHeight = map((float)HEIGHT, 8, 32, 28, 12);
    adjScale = map((int)WIDTH, 8, 32, 310, 127);
  }
byte _speed = 16;
 _scale = adjScale-55;
  for (byte x = 0; x < WIDTH; x++) {
    for (byte y = 0; y < HEIGHT; y++) {
      timer++;
      uint16_t i = x*y;
      leds[XY(x, y)]= 
          ColorFromPalette(*curPalette,
            qsub8(
              inoise8(i % 2 + x * _scale,
                y * 16 + timer % 16,
                timer / _speed
              ),
              fabs((float)HEIGHT/2. - (float)y) * adjastHeight
            )
          );
         
        //CRGB temColor = leds[XY(x, y)];
        //leds[XY(x, y)].g = temColor.r;
        //leds[XY(x, y)].r = temColor.g;
        //leds[XY(x, y)].g /= 6;
        //leds[XY(x, y)].r += leds[XY(x, y)].r < 206 ? 48 : 0;
        //        leds[XY(x, y)].b += 48;
        //leds[XY(x, y)].g += leds[XY(x, y)].g < 206 ? 48 : 0;
    }}}

 //loop

byte dist (uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)  {
  int a = y2 - y1;
  int b = x2 - x1;
  a *= a;
  b *= b;
  byte dist = 220 / sqrt16(a + b);
  return dist;
}

void Wave() {
  if (loadingFlag)
  { loadingFlag = false;
    setCurrentPalette(pal);
  }
    FastLED.clear();
    for(byte i=0; i < NUM_COLS; i++) {
      byte thisVal = inoise8(i * 45 ,millis(), millis());
      byte thisMax = map(thisVal, 0, 255, 0, NUM_ROWS);
      for(byte j = 0; j < thisMax; j++) {
        leds[XY(i, j)] = ColorFromPalette(*curPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND);
        leds[XY((NUM_COLS-1)-i, (NUM_ROWS-1)-j)] = ColorFromPalette(*curPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND);
      }}   
}


void draw() {
  switch (type) {
    case 0: Enoise(); break;
    case 1: drop(); break;
    case 2: rainbowHorVertRoutine(true); break;
    case 3: meta(); break;
    case 4: Fire(); break;
    case 5: aurora();break;
    case 6: Wave();break;
  }
}

void setup() {
  //Serial.begin(250000);
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  pinMode(2, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
}

void loop() {
  bool buttonPressed = digitalRead(2) == LOW;
  bool buttonPressed2 = digitalRead(4) == LOW;
  draw();
  if (buttonPressed) {
    if (pal >= 21) {
      pal = 0;
    } else {
      pal += 1;
    }
    FastLED.clear();
    loadingFlag = true;
    delay(100);
  } else if (buttonPressed2) {
    if (type >= 6) {
      type = 0;
    } else {
      type += 1;
    }
    FastLED.clear();
    loadingFlag = true;
    delay(100);
  } else {
    FastLED.show();
    FastLED.delay(1000 / 60);
  }
  //static int frame = 0;
  //if (frame++ % 32 == 0)
  // Serial.println(FastLED.getFPS());

} //loop


uint16_t XY (uint8_t x, uint8_t y) {
  return (y * NUM_COLS + x);
}
FPS: 0
Power: 0.00W