#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
// LEDs pin
#define DATA_PIN 3
// LED brightness
#define BRIGHTNESS 255
// Define the array of leds
CRGB leds[NUM_LEDS];

//Holiday lights
//@stepko
//Merry Christmas and Happy New Year
byte eff = 0;
#define WIDTH NUM_COLS
#define HEIGHT NUM_ROWS
#define speed (100/(HEIGHT-4))
byte scale;
void drawPixelXYF_X(float x, uint16_t y, const CRGB &color)
  {
  //if (x<0 || y<0 || x>((float)WIDTH) || y>((float)HEIGHT)) return;

  // extract the fractional parts and derive their inverses
  uint8_t xx = (x - (int)x) * 255, ix = 255 - xx;
  // calculate the intensities for each affected pixel
  uint8_t wu[2] = {ix, xx};
  // multiply the intensities by the colour, and saturating-add them to the pixels
  for (int8_t i = 1; i >= 0; i--) {
      int16_t xn = x + (i & 1);
      CRGB clr = leds[XY(xn, y)];
      if(xn>0 && xn<(int)WIDTH-1){
        clr.r = qadd8(clr.r, (color.r * wu[i]) >> 8);
        clr.g = qadd8(clr.g, (color.g * wu[i]) >> 8);
        clr.b = qadd8(clr.b, (color.b * wu[i]) >> 8);
      } else if(xn==0 || xn==(int)WIDTH-1) {
        clr.r = qadd8(clr.r, (color.r * 85) >> 8);
        clr.g = qadd8(clr.g, (color.g * 85) >> 8);
        clr.b = qadd8(clr.b, (color.b * 85) >> 8);
      }
      leds[XY(xn, y)] = clr;
  }
  }
  
byte y[HEIGHT];
byte x[WIDTH];
int fx[WIDTH];
byte hue;
void Radar() {
  hue++;
  fadeToBlackBy(leds, NUM_LEDS, 2);
  scale = beatsin8(1,1,255);
  for (int i = 0; i < WIDTH-1; i++) {
  y[i] = i;
  fx[i] = beatsin16(speed*5,i*2.5,((WIDTH-1)*5)-i*2.5);
  drawPixelXYF_X((float)fx[i]/5,y[i],CHSV(hue+i*scale,255,100));
}}
void Wave() {
  hue++;
  fadeToBlackBy(leds, NUM_LEDS, 2);
  scale = beatsin8(1,1,255);
  for (int i = 0; i < WIDTH-1; i++) {
  y[i] = i;
  fx[i] = beatsin16(i*speed,i*5,((WIDTH-1)*10-2)-(i*5+2));
  drawPixelXYF_X((float)fx[i]/10,y[i],CHSV(hue+i*scale,255,100));
}}
void Spark(){
  fadeToBlackBy(leds, NUM_LEDS, 3);
  blur2d(leds, WIDTH, HEIGHT, 20);
  for (int i = 1; i < WIDTH-1; i++) {
  fx[i] = random(i*2,((WIDTH-1)*4)-(i*2));
  y[i] = i;
  if(leds[XY(round(fx[i]/4),y[i])] == CRGB(0,0,0))
  drawPixelXYF_X((float)fx[i]/4,y[i], CHSV(random8(),255,255));}
}
void NormalTree() {//by Unknown User https://editor.soulmatelights.com/gallery/552
  hue++;
  fadeToBlackBy(leds, NUM_LEDS, 5);
  scale = beatsin8(1,1,255);
  for (int i = 0; i < WIDTH-1; i++) {
  y[i] = i;
  x[i] = beatsin16(i * speed , i * 2,((WIDTH-1)*4-2)-(i*2+2));
  drawPixelXYF_X(x[i] / 4, y[i], random8(10) == 0 ? CHSV(random(0, 255), random8(32, 255), 255) : CHSV(100, 255, 100));
  }
}

void draw() {
  switch (eff) {
    case 0: Spark(); break;
    case 1: Wave(); break;
    case 2: NormalTree(); break;
    case 3: Radar(); break;
  }
}



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

void loop() {
  draw();
  bool buttonPressed = digitalRead(2) == LOW;
  draw();
  if (buttonPressed) {
    if (eff >= 3) {
      eff = 0;
    } else {
      eff += 1;
    }
    FastLED.clear();
    delay(100);
  } else {
    FastLED.show();
  }
  //static int frame = 0;
  //if (frame++ % 32 == 0)
  //  Serial.println(FastLED.getFPS());

} //loop


uint16_t XY (uint8_t x, uint8_t y) {
  return ((HEIGHT - y - 1) * NUM_COLS + x);
}