#include <FastLED.h>
 
#define TIMING 1

#define LED_PIN     3
#define NUM_LEDS    1630
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB

CRGB leds[NUM_LEDS];
const uint8_t led_count[] = {160, 145, 142, 135, 125, 118, 110, 102, 95, 86, 78, 70, 62, 53, 45, 37, 29, 21, 12, 4, 1};

#define NUM_RINGS (sizeof(led_count) / sizeof(led_count[0]))
#define FIRE_WIDTH 64
#define FIRE_HEIGHT NUM_RINGS
static uint8_t heat[FIRE_WIDTH][FIRE_HEIGHT];
CRGBPalette256 currentPalette;

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  uint8_t i = 0;
  do {
    currentPalette[i] = HeatColor(i);
  } while (++i);
}


void loop() {
	unsigned long t1 = micros();
  Fire2012(127 + random8(128));
	unsigned long t2 = micros();

  CRGB *led = leds;
  uint8_t ring = 0;
  // map the modified Fire2012() onto the rings
  do {
    uint8_t count = led_count[ring];
    uint16_t const td = FIRE_WIDTH * 256 / count;
    uint16_t t = 0;
    for (uint8_t i = 0; i < count ; i++) {
      uint8_t h = heat[t >> 8][FIRE_HEIGHT - 1 - ring];
      *led++ = currentPalette[h];
      t += td;
    }
  } while (++ring < NUM_RINGS);
	unsigned long t3 = micros();

	unsigned long t4 = millis() * 1000;
  FastLED.show();
  unsigned long t5 = millis() * 1000;

  if (TIMING) {
    static unsigned long t2_sum, t3_sum, t5_sum;
    t2_sum += t2 - t1;
    t3_sum += t3 - t2;
    t5_sum += t5 - t4;
    static byte frame;
    if (!(++frame % 64)) {
      Serial.print(F("fire "));
      Serial.print(t2_sum / 64);
      Serial.print(F("us\tmap "));
      Serial.print(t3_sum / 64);
      Serial.print(F(" us\tshow "));
      Serial.print(t5_sum / 64);
      Serial.print(F(" us\tFPS "));
      // Serial.println(FastLED.getFPS());
      Serial.println(1000000.f * 64 / (t2_sum + t3_sum + t5_sum), 2);
      t2_sum = t3_sum = t5_sum = 0;
    }
  }
}

void Fire2012(uint8_t activity) {
  for (uint8_t x = 0; x < FIRE_WIDTH; x++) {
    // Step 1.  Cool down every cell a little
    // for( uint8_t y = 0; y < FIRE_HEIGHT; y++) {
    //   heat[x][y] = qsub8( heat[x][y],  20);
    // }

    if( random8() < activity ) {
      heat[x][0] = qadd8( heat[x][0], random8(50));
    } else {
      heat[x][0] = qsub8( heat[x][0], 64);
    }

    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    uint8_t xleft = (x + FIRE_WIDTH - 1) % FIRE_WIDTH;
    uint8_t xright = (x + 1) % FIRE_WIDTH;

    for( uint8_t y = FIRE_HEIGHT - 1; y >= 2; y--) {
      uint16_t sum = (
                    heat[x][y]
                  + heat[xleft][y - 1]
                  + heat[x][y - 1]
                  + heat[xright][y - 1]
                  + heat[x][y - 1]
                  + heat[xleft][y - 2]
                  + heat[x][y - 2]
                  + heat[xright][y - 2] );
        heat[x][y] = sum > 64 ? (sum - 64) / 8 : 0;
    }

    uint16_t sum = (
                    heat[x][1]
                  + heat[xleft][0]
                  + heat[x][0]
                  + heat[xright][0]
                  );
    heat[x][1] = sum > 64 ? (sum - 64) / 4 : 0;
    

  }
}