#include <FastLED.h>

#define WIDTH 16
#define HEIGHT 16
#define NUM_LEDS ((WIDTH) * (HEIGHT))
#define POINTS 6

CRGB leds[NUM_LEDS + 1];

uint16_t XY(uint8_t x, uint8_t y) {
  if (x >= WIDTH) return NUM_LEDS;
  if (y >= HEIGHT) return NUM_LEDS;
  if (y & 1)
    return (y + 1) * WIDTH - 1 - x;
  else
    return y * WIDTH + x;
}

#include "wuLineAA.h"


CRGBPalette16 currentPalette = {
  0xFF0000, 0x7F0000, 0xAB5500, 0x552A00,
  0xABAB00, 0x555500, 0x00FF00, 0x007F00,
  0x00AB55, 0x00552A, 0x0000FF, 0x00007F,
  0x5500AB, 0x2A0055, 0xAB0055, 0x55002A
};

// x & y are Q7.8 fixed-point
// dx & dy are Q.7 deltas for x & y.
struct Point {
  saccum78 x, y;
  sfract7 dx, dy;
};
Point points[POINTS];


void setup() {
  FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS);
  Serial.begin(115200);
  for (uint8_t i = 0; i < POINTS; i++) {
    points[i].x = random16() % (WIDTH << 8);
    points[i].y = random16() % (HEIGHT << 8);
    points[i].dx = (random8() - 128) / 24;
    points[i].dy = (random8() - 128) / 24;
  }
}


void loop()
{
  static int frame; frame++;
  uint32_t ms = millis();
  FastLED.clear();
  // fadeToBlackBy(leds, NUM_LEDS, 25);


  // change `effect` every 3-seconds
  static uint8_t effect = 0;
  EVERY_N_MILLIS(3000) {
    // Serial.println(FastLED.getFPS());
    if (++effect > 2) effect = 2;
  }

  // // move the points, and bounce off the edges
  // for (uint8_t i = 0; i < POINTS; i++) {
  //   points[i].x += points[i].dx;
  //   points[i].y += points[i].dy;
  //   if (points[i].x <= 0) {
  //     points[i].dx = -points[i].dx;
  //     points[i].x = -points[i].x;
  //   } else if (points[i].x >= WIDTH << 8) {
  //     points[i].dx = -points[i].dx;
  //     points[i].x = (WIDTH << 9) - points[i].x;
  //   }
  //   if (points[i].y <= 0) {
  //     points[i].dy = -points[i].dy;
  //     points[i].y = -points[i].y;
  //   } else if (points[i].y >= HEIGHT << 8) {
  //     points[i].dy = -points[i].dy;
  //     points[i].y = (HEIGHT << 9) - points[i].y;
  //   }
  // }

  uint16_t centrex = WIDTH * 128;
  uint16_t centrey = HEIGHT * 128;

  for (uint8_t i = 0; i < POINTS; i++) {
    points[i].x = cos16(ms * 8 + i * (65536 / POINTS)) / (256 / WIDTH + 2) + centrex;
    points[i].y = sin16(ms * 8 + i * (65536 / POINTS)) / (256 / HEIGHT + 2) + centrey;
  }


  // make a colour
  // bigger multipliers change the colour faster
  // use prime numbers as the multipliers for the longest loop-time
  CRGB plot_colour = CRGB(128 + sin16(ms * 13) / 256,
                          128 + sin16(ms * 17) / 256,
                          128 + sin16(ms * 29) / 256);
  plot_colour = 0xffffff;

  // draw lines between each point
  for (uint8_t i = 0; i < POINTS; i++) {
    switch (effect) {
      // show aliased lines
      case 0:
        plotLine( points[i].x >> 8,
                  points[i].y >> 8,
                  points[(i + 1) % POINTS].x >> 8,
                  points[(i + 1) % POINTS].y >> 8,
                  &plot_colour);
        break;
      //  ...and anti-aliased
      case 1:
        plotLineAA( points[i].x >> 8,
                    points[i].y >> 8,
                    points[(i + 1) % POINTS].x >> 8,
                    points[(i + 1) % POINTS].y >> 8,
                    &plot_colour);
        break;
      default:
      // ...and sub-pixel positioned anti-aliased
        wuLineAA( points[i].x,
                  points[i].y,
                  points[(i + 1) % POINTS].x,
                  points[(i + 1) % POINTS].y,
                  &plot_colour);
    }
  }


  // wuLineAA( 0, 0, (WIDTH << 8) -1, frame % (HEIGHT << 8), &plot_colour);
  FastLED.show();
  FastLED.delay(50);
}






// from: https://github.com/FastLED/FastLED/pull/202
CRGB ColorFromPaletteExtended(const CRGBPalette16& pal, uint16_t index, uint8_t brightness, TBlendType blendType) {
  // Extract the four most significant bits of the index as a palette index.
  uint8_t index_4bit = (index >> 12);
  // Calculate the 8-bit offset from the palette index.
  uint8_t offset = (uint8_t)(index >> 4);
  // Get the palette entry from the 4-bit index
  const CRGB* entry = &(pal[0]) + index_4bit;
  uint8_t red1   = entry->red;
  uint8_t green1 = entry->green;
  uint8_t blue1  = entry->blue;

  uint8_t blend = offset && (blendType != NOBLEND);
  if (blend) {
    if (index_4bit == 15) {
      entry = &(pal[0]);
    } else {
      entry++;
    }

    // Calculate the scaling factor and scaled values for the lower palette value.
    uint8_t f1 = 255 - offset;
    red1   = scale8_LEAVING_R1_DIRTY(red1,   f1);
    green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
    blue1  = scale8_LEAVING_R1_DIRTY(blue1,  f1);

    // Calculate the scaled values for the neighbouring palette value.
    uint8_t red2   = entry->red;
    uint8_t green2 = entry->green;
    uint8_t blue2  = entry->blue;
    red2   = scale8_LEAVING_R1_DIRTY(red2,   offset);
    green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
    blue2  = scale8_LEAVING_R1_DIRTY(blue2,  offset);
    cleanup_R1();

    // These sums can't overflow, so no qadd8 needed.
    red1   += red2;
    green1 += green2;
    blue1  += blue2;
  }
  if (brightness != 255) {
    // nscale8x3_video(red1, green1, blue1, brightness);
    nscale8x3(red1, green1, blue1, brightness);
  }
  return CRGB(red1, green1, blue1);
}