#include "FastLED.h"

// Matrix size
#define NUM_ROWS 32
#define NUM_COLS 32
#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];

#define scale (8)
#define board (0)
#define trace (1)
#define Go_horizontal (1)
#define LIGHTERS_AM WIDTH*2
int lightersPosX[LIGHTERS_AM];
int lightersPosY[LIGHTERS_AM];
float lightersSpeedX[LIGHTERS_AM];
float lightersSpeedY[LIGHTERS_AM];
byte lcolor[LIGHTERS_AM];
byte mass[LIGHTERS_AM];

void drawPixelXYF(float x, float y, CRGB color)
{
  // if (x < 0 || y < 0 || x > ((float)WIDTH - 1) || y > ((float)HEIGHT - 1)) return;
  uint8_t xx = (x - (int)x) * 255, yy = (y - (int)y) * 255, ix = 255 - xx, iy = 255 - yy;
  // calculate the intensities for each affected pixel
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
  uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy),
                   WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)
                  };
  // multiply the intensities by the colour, and saturating-add them to the pixels
  for (uint8_t i = 0; i < 4; i++) {
    int16_t xn = x + (i & 1), yn = y + ((i >> 1) & 1);
    CRGB clr = leds[XY(xn, yn)];
    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);
    leds[XY(xn, yn)] = clr;
  }
}
void drawPixelXYF_Y(uint16_t x, float 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 yy = (y - (int)y) * 255, iy = 255 - yy;
  // calculate the intensities for each affected pixel
  uint8_t wu[2] = {iy, yy};
  // multiply the intensities by the colour, and saturating-add them to the pixels
  for (int8_t i = 1; i >= 0; i--) {
      int16_t yn = y + (i & 1);
      CRGB clr = leds[XY(x, yn)];
      if(yn>0 && yn<(int)HEIGHT-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(yn==0 || yn==(int)HEIGHT-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(x, yn)] = clr;
  }
}

bool loadingFlag = true;
bool boom;
void reg(byte id) {
  if (Go_horizontal)lightersSpeedX[id] = random(-20, 20);else lightersSpeedX[id] = 0;
  lightersSpeedY[id] = random(20);
  lcolor[id] = random();
}

void phisics(byte id) {
  // lcolor[id] = random();
  lightersPosX[id] += lightersSpeedX[id];
  lightersPosY[id] += lightersSpeedY[id];
  lightersSpeedY[id] -= .98*mass[id]/128;
  if (lightersSpeedX[id] < 0 || lightersSpeedX[id] > 0) {
    if (lightersSpeedX[id] > 0)
      lightersSpeedX[id] -= 0.1;
    else
      lightersSpeedX[id] += 0.1;
  }
  if (lightersPosY[id] < 0) {
    lightersSpeedY[id] = -lightersSpeedY[id] * 0.9;
  }
  if(board){
  if (lightersPosX[id] <= 0 || lightersPosX[id] >= (WIDTH-1) * 10) {
    lightersSpeedX[id] = -lightersSpeedX[id];
  }}else{if (lightersPosX[id] < 0)lightersPosX[id] = (WIDTH-1)*10;if (lightersPosX[id] > (WIDTH-1)*10)lightersPosX[id] = 0;}
  if (lightersSpeedY[id] > 0 && lightersSpeedY[id]< 1 && lightersPosY[id]<20)
    reg(id);
}

void draw() {
  if (loadingFlag) {
    for (byte i = 0; i < map(scale, 1, 16, 1, LIGHTERS_AM); i++) {
      reg(i);
      mass[i] = random(64,255);
      lightersPosX[i] = random(0,(WIDTH-1)*10);
      lightersPosY[i] = random(0,(HEIGHT-1)*10);
    }
    loadingFlag = false;
  }
  
  if(trace)fadeToBlackBy(leds, NUM_LEDS, 80);else FastLED.clear();
  for (byte i = 0; i < map(scale, 1, 16, 1, LIGHTERS_AM); i++) {
    phisics(i);
    if (lightersPosY[i] < ((HEIGHT - 1) * 10)){
  if (lightersPosX[i] < ((WIDTH - 1) * 10) && lightersPosX[i] > 0)
  if(lightersSpeedX[i]==0)
 drawPixelXYF_Y((float)lightersPosX[i] / 10, ((float)lightersPosY[i] / 10), CHSV(lcolor[i], 255, 255));
      else drawPixelXYF((float)lightersPosX[i] / 10, ((float)lightersPosY[i] / 10), CHSV(lcolor[i], 255, 255));
  }}
}

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

void loop() {
  draw();
  FastLED.show();
  //static int frame = 0;
  //if (frame++ % 32 == 0)
  //  Serial.println(FastLED.getFPS());

} //loop



uint16_t XY (uint8_t x, uint8_t y) {

  if ((y % 2 == 0) || MATRIX_TYPE)                     // если чётная строка
  {
    return ((uint32_t)y * WIDTH + x) % (WIDTH * HEIGHT);
  }
  else                                                      // если нечётная строка
  {
    return ((uint32_t)y  * WIDTH + WIDTH - x - 1) % (WIDTH * HEIGHT);
  }
}