#include "FastLED.h"

// Matrix size
#define WIDTH 32
#define HEIGHT 32
#define NUM_LEDS HEIGHT * WIDTH
#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 (16)
#define LIGHTERS_AM WIDTH
float lightersPos[2][LIGHTERS_AM];
float lightersSpeed[2][LIGHTERS_AM];
float sparksSat[LIGHTERS_AM];    
float sparksFade[LIGHTERS_AM];
byte sparksColor[LIGHTERS_AM];
static byte period = 10;
int gPos[2];
bool run = true;

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;
  }
}


bool loadingFlag = true;
void reg(byte id) {
  lightersPos[0][id] = gPos[0];
  lightersPos[1][id] = gPos[1];
  lightersSpeed[0][id] = random(-10, 10);
  lightersSpeed[1][id] = random(-5, 20);
  sparksSat[id] = 10;
  sparksFade[id] = 255;
  sparksColor[id] = random();
}


void phisics(byte id) {
  // lcolor[id] = random();
  lightersPos[0][id] += lightersSpeed[0][id];
  lightersPos[1][id] += lightersSpeed[1][id];
  lightersSpeed[1][id] -= .98;
  sparksSat[id] += (255. / (float)WIDTH);
  sparksFade[id] -= (255. / (float)(HEIGHT*1.5));
  if (lightersSpeed[0][id] < 0 || lightersSpeed[0][id] > 0) {
    if (lightersSpeed[0][id] > 0)
      lightersSpeed[0][id] -= 0.1;
    else
      lightersSpeed[0][id] += 0.1;
  }
  if (lightersPos[0][id] <= 0 || lightersPos[0][id] >= WIDTH * 10 || lightersPos[1][id] < 0) {
    reg(id);
  }
}

void draw() {
  if (loadingFlag) {
    for (byte i = 0; i < map(scale, 1, 16, 1, LIGHTERS_AM); i++) {
      reg(i);
    }
    gPos[1] = (HEIGHT / 2) * 10;
    gPos[0] = (WIDTH / 2) * 10;
    loadingFlag = false;
  }
  //FastLED.clear();
  fadeToBlackBy(leds, NUM_LEDS, beatsin8(5, 20, 100));
  if (run) {
    gPos[1] = beatsin16(5, 0, HEIGHT * 10);
    gPos[0] = beatsin16(6, 0, WIDTH * 10);
  }else{
    gPos[1] = (HEIGHT / 2) * 10;
    gPos[0] = (WIDTH / 2) * 10;
  }
  for (byte i = 0; i < map(scale, 1, 16, 1, LIGHTERS_AM); i++) {
    phisics(i);
    if (lightersPos[1][i] < ((HEIGHT - 1) * 10))
	      if (lightersPos[0][i] < ((WIDTH - 1) * 10) and lightersPos[0][i] >= 0)
      drawPixelXYF(lightersPos[0][i] / 10, (lightersPos[1][i] / 10), CHSV(sparksColor[i], constrain(sparksSat[i], 5, 255), constrain(sparksFade[i], 32, 255)));
  }
  EVERY_N_SECONDS(period) {
    for (byte i = 0; i < LIGHTERS_AM; i++)reg(i);
    period = random(10, 60);
  }
}



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

void loop() {
  draw();
  FastLED.show();
} //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);
  }
}