#include <FastLED.h>

#define WIDTH 22
#define HEIGHT 22
#define NUM_LEDS ((WIDTH) * (HEIGHT))
#define POINTS 5

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"

// 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() {
  Serial.begin(115200);
  FastLED.addLeds<NEOPIXEL, 3>(leds, NUM_LEDS);
  FastLED.setCorrection(UncorrectedColor);
  FastLED.setTemperature(UncorrectedTemperature);
  FastLED.setDither(DISABLE_DITHER);
}


void loop()
{
  static int frame; frame++;
  uint32_t ms = millis();
  FastLED.clear();

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

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


  CRGB 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;
      case 2:
      // 1/2 sub-pixel
        wuLineAA( points[i].x & 0xff80,
                  points[i].y & 0xff80,
                  points[(i + 1) % POINTS].x & 0xff80,
                  points[(i + 1) % POINTS].y & 0xff80,
                  &plot_colour);
        break;
      case 3:
      // 1/4 sub-pixel
        wuLineAA( points[i].x & 0xffc0,
                  points[i].y & 0xffc0,
                  points[(i + 1) % POINTS].x & 0xffc0,
                  points[(i + 1) % POINTS].y & 0xffc0,
                  &plot_colour);
        break;
      case 4:
      // 1/8th sub-pixel
        wuLineAA( points[i].x & 0xffe0,
                  points[i].y & 0xffe0,
                  points[(i + 1) % POINTS].x & 0xffe0,
                  points[(i + 1) % POINTS].y & 0xffe0,
                  &plot_colour);
        break;
      case 5:
      // 1/16th sub-pixel
        wuLineAA( points[i].x & 0xfff0,
                  points[i].y & 0xfff0,
                  points[(i + 1) % POINTS].x & 0xfff0,
                  points[(i + 1) % POINTS].y & 0xfff0,
                  &plot_colour);
        break;
      default:
      // ...and 1/256th sub-pixel positioned anti-aliased
        wuLineAA( points[i].x,
                  points[i].y,
                  points[(i + 1) % POINTS].x,
                  points[(i + 1) % POINTS].y,
                  &plot_colour);
    }
  }


  FastLED.show();
  // delay(1000);
}






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