#include <FastLED.h>

#define LED_PIN  3

#define COLOR_ORDER GRB
#define CHIPSET     WS2811

#define BRIGHTNESS 255

#include <colorpalettes.h>
#include "palettes.h"

// Helper functions for an two-dimensional XY matrix of pixels.
// Simple 2-D demo code is included as well.
//
//     XY(x,y) takes x and y coordinates and returns an LED index number,
//             for use like this:  leds[ XY(x,y) ] == CRGB::Red;
//             No error checking is performed on the ranges of x and y.
//
//     XYsafe(x,y) takes x and y coordinates and returns an LED index number,
//             for use like this:  leds[ XY(x,y) ] == CRGB::Red;
//             Error checking IS performed on the ranges of x and y, and an
//             index of "-1" is returned.  Special instructions below
//             explain how to use this without having to do your own error
//             checking every time you use this function.
//             This is a slightly more advanced technique, and
//             it REQUIRES SPECIAL ADDITIONAL setup, described below.


// Params for width and height
const uint8_t kMatrixWidth = 16;
const uint8_t kMatrixHeight = 16;

// Param for different pixel layouts
const bool    kMatrixSerpentineLayout = true;
// Set 'kMatrixSerpentineLayout' to false if your pixels are
// laid out all running the same way, like this:
//
//     0 >  1 >  2 >  3 >  4
//                         |
//     .----<----<----<----'
//     |
//     5 >  6 >  7 >  8 >  9
//                         |
//     .----<----<----<----'
//     |
//    10 > 11 > 12 > 13 > 14
//                         |
//     .----<----<----<----'
//     |
//    15 > 16 > 17 > 18 > 19
//
// Set 'kMatrixSerpentineLayout' to true if your pixels are
// laid out back-and-forth, like this:
//
//     0 >  1 >  2 >  3 >  4
//                         |
//                         |
//     9 <  8 <  7 <  6 <  5
//     |
//     |
//    10 > 11 > 12 > 13 > 14
//                        |
//                        |
//    19 < 18 < 17 < 16 < 15
//
// Bonus vocabulary word: anything that goes one way
// in one row, and then backwards in the next row, and so on
// is call "boustrophedon", meaning "as the ox plows."


// This function will return the right 'led index number' for
// a given set of X and Y coordinates on your matrix.
// IT DOES NOT CHECK THE COORDINATE BOUNDARIES.
// That's up to you.  Don't pass it bogus values.
//
// Use the "XY" function like this:
//
//    for( uint8_t x = 0; x < kMatrixWidth; x++) {
//      for( uint8_t y = 0; y < kMatrixHeight; y++) {
//
//        // Here's the x, y to 'led index' in action:
//        leds[ XY( x, y) ] = CHSV( random8(), 255, 255);
//
//      }
//    }
//
//
uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;

  if( kMatrixSerpentineLayout == false) {
    i = (y * kMatrixWidth) + x;
  }

  if( kMatrixSerpentineLayout == true) {
    if( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (kMatrixWidth - 1) - x;
      i = (y * kMatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * kMatrixWidth) + x;
    }
  }

  return i;
}


// Once you've gotten the basics working (AND NOT UNTIL THEN!)
// here's a helpful technique that can be tricky to set up, but
// then helps you avoid the needs for sprinkling array-bound-checking
// throughout your code.
//
// It requires a careful attention to get it set up correctly, but
// can potentially make your code smaller and faster.
//
// Suppose you have an 8 x 5 matrix of 40 LEDs.  Normally, you'd
// delcare your leds array like this:
//    CRGB leds[40];
// But instead of that, declare an LED buffer with one extra pixel in
// it, "leds_plus_safety_pixel".  Then declare "leds" as a pointer to
// that array, but starting with the 2nd element (id=1) of that array:
//    CRGB leds_with_safety_pixel[41];
//    CRGB* const leds( leds_plus_safety_pixel + 1);
// Then you use the "leds" array as you normally would.
// Now "leds[0..N]" are aliases for "leds_plus_safety_pixel[1..(N+1)]",
// AND leds[-1] is now a legitimate and safe alias for leds_plus_safety_pixel[0].
// leds_plus_safety_pixel[0] aka leds[-1] is now your "safety pixel".
//
// Now instead of using the XY function above, use the one below, "XYsafe".
//
// If the X and Y values are 'in bounds', this function will return an index
// into the visible led array, same as "XY" does.
// HOWEVER -- and this is the trick -- if the X or Y values
// are out of bounds, this function will return an index of -1.
// And since leds[-1] is actually just an alias for leds_plus_safety_pixel[0],
// it's a totally safe and legal place to access.  And since the 'safety pixel'
// falls 'outside' the visible part of the LED array, anything you write
// there is hidden from view automatically.
// Thus, this line of code is totally safe, regardless of the actual size of
// your matrix:
//    leds[ XYsafe( random8(), random8() ) ] = CHSV( random8(), 255, 255);
//
// The only catch here is that while this makes it safe to read from and
// write to 'any pixel', there's really only ONE 'safety pixel'.  No matter
// what out-of-bounds coordinates you write to, you'll really be writing to
// that one safety pixel.  And if you try to READ from the safety pixel,
// you'll read whatever was written there last, reglardless of what coordinates
// were supplied.

#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
CRGB leds_plus_safety_pixel[ NUM_LEDS + 1];
CRGB* const leds( leds_plus_safety_pixel + 1);

uint16_t XYsafe( uint8_t x, uint8_t y)
{
  if( x >= kMatrixWidth) return -1;
  if( y >= kMatrixHeight) return -1;
  return XY(x,y);
}


// Demo that USES "XY" follows code below

void loop()
{
  plasma();
 //   uint32_t ms = millis();
   // int32_t yHueDelta32 = ((int32_t)cos16( ms * (27/1) ) * (350 / kMatrixWidth));
   // int32_t xHueDelta32 = ((int32_t)cos16( ms * (39/1) ) * (310 / kMatrixHeight));
   // DrawOneFrame( ms / 65536, yHueDelta32 / 32768, xHueDelta32 / 32768);
    
    FastLED.show();
}

void DrawOneFrame( byte startHue8, int8_t yHueDelta8, int8_t xHueDelta8)
{
  byte lineStartHue = startHue8;
  for( byte y = 0; y < kMatrixHeight; y++) {
    lineStartHue += yHueDelta8;
    byte pixelHue = lineStartHue;
    for( byte x = 0; x < kMatrixWidth; x++) {
      pixelHue += xHueDelta8;
      leds[ XY(x, y)]  = CHSV( pixelHue, 255, 255);
    }
  }
}


void setup() {
  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
  //FastLED.addLeds<NEOPIXEL, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
  //FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
  FastLED.setBrightness( BRIGHTNESS );
   scaleBright(bright_p);

}



void fire() {
  int  a = millis() + msOffset;
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++) {
      if(rows-1-j < rows+intense){
        if(i>1&&i<15){
          uint8_t fireVal = qsub8 (inoise8 (i * shape , j * shape+ a , a * 2 / (100/firespeed) ), abs8(j - (rows-1)) * 255 / (rows+3+intense));
          leds[XY(j,i)] = ColorFromPalette (currentPal, fireVal , 255, LINEARBLEND );
          }
        else{
           // uint8_t fireVal1  = qsub8 (inoise8 (1 * shape , j * shape+ a , a * 2 / (100/firespeed) ), abs8(j - (rows-1)) * 255 / (rows+3+intense));
           // uint8_t fireVal15 = qsub8 (inoise8 (15 * shape , j * shape+ a , a * 2 / (100/firespeed) ), abs8(j - (rows-1)) * 255 / (rows+3+intense));
           // if(i == 15){ leds[XY(j,i)] = ColorFromPalette (currentPal, 0.75 * fireVal15 + 0.25 * fireVal1 , 255, LINEARBLEND );} //
           // if(i == 0){ leds[XY(j,i)] = ColorFromPalette (currentPal, 0.50 * fireVal15 + 0.50 * fireVal1, 255, LINEARBLEND );} //
           // if(i == 1){leds[XY(j,i)] = ColorFromPalette (currentPal, 0.25 * fireVal15 + 0.75 * fireVal1, 255, LINEARBLEND );} //
        }
     }
      else{leds[XY(j,i)] = CRGB::Black;}
    }
  } 
}

void scaleBright(int val){
  if(val != 100) val = wrap_int(val,100);
  if(val == 0 && lampeAn){lampeAn = false; }
  if(bright_p = 0 && val > 0 && !lampeAn){lampeAn = true; }
  bright_p = val;
  shape = map(val,0,100,40,110);
  firespeed = map(val,0,100,25,50);
  intense = map(val,0,100,-10,8);
  if(intense > 0) intense = 0;
  bright = map(val,0,100,25,255);
  FastLED.setBrightness(bright);
}
void plasma(){
    static int minNoise = 0;
    static int maxNoise = 0;
    float plasmaSpeed = 25;//0.25; //0.1 to 1
    int plasmaZoom = 2800; // 20 to 80
    uint32_t ms = plasmaSpeed * millis() + msOffset; 

  for (int x = 0; x < cols; x++) {
    for (int y = 0; y < rows; y++) {
      if(y>1&&y<15){
          int noiseVal = inoise16_raw(y * plasmaZoom, x * plasmaZoom, ms);
          if(noiseVal > maxNoise){maxNoise = noiseVal; }
          else if(noiseVal < minNoise){minNoise = noiseVal; }
          byte noises =  map(noiseVal,minNoise,maxNoise,0,255);
          leds[XY(x,y)] = ColorFromPalette(currentPal, noises, bright, LINEARBLEND);
          }
      else{
          int noiseVal1 = inoise16_raw(1 * plasmaZoom, x * plasmaZoom, ms);
          if(noiseVal1 > maxNoise){maxNoise = noiseVal1; }
          else if(noiseVal1 < minNoise){minNoise = noiseVal1; }
          byte noises1 =  map(noiseVal1,minNoise,maxNoise,0,255);
          
          int noiseVal15 = inoise16_raw(15 * plasmaZoom, x * plasmaZoom, ms);
          if(noiseVal15 > maxNoise){maxNoise = noiseVal15; }
          else if(noiseVal15 < minNoise){minNoise = noiseVal15; }
          byte noises15 =  map(noiseVal15,minNoise,maxNoise,0,255);
            if(y == 15){ leds[XY(x,y)] = ColorFromPalette(currentPal, 0.75 * noises15 + 0.25 * noises1, bright, LINEARBLEND); } //
            if(y == 0) { leds[XY(x,y)] = ColorFromPalette(currentPal, 0.50 * noises15 + 0.50 * noises1, bright, LINEARBLEND );} //
            if(y == 1) { leds[XY(x,y)] = ColorFromPalette(currentPal, 0.25 * noises15 + 0.75 * noises1, bright, LINEARBLEND );} //
      }
    }
  }
}
unsigned int wrap_int(int val, int maxVal){ return abs(val) % abs(maxVal);}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
neopixels:DOUT
neopixels:VDD
neopixels:VSS
neopixels:DIN