#include <Arduino.h>
#include <FastLED.h>
#include <colorutils.h>

enum XY_matrix_config {
  SERPENTINE = 1,
  ROWMAJOR = 2,
  FLIPMAJOR = 4,
  FLIPMINOR = 8
};

#define BRIGHTNESS    255

// Define LED matrix parameters
#define LED_PIN       3
#define COLOR_ORDER   GRB
#define CHIPSET       WS2812B
#define MATRIX_WIDTH  40
#define MATRIX_HEIGHT 12
#define NUM_LEDS    (MATRIX_WIDTH * MATRIX_HEIGHT)
#define NUM_LINES    2    // the number of lines you are displaying

#define TEST_UI          1    // allow output to the serial console for testing

CRGB leds[NUM_LEDS + 1];  // 1 extra for XY() to use when out-of-bounds

CRGBPalette16 currentPalette = RainbowColors_p;

typedef struct {
  int16_t x1, y1, x2, y2;
} LineXY; 

LineXY linesXY[NUM_LINES];


void setup() {
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setCorrection(UncorrectedColor);          //optional but turns off any previously set color correction
  FastLED.setTemperature(UncorrectedTemperature);   //optional but turns of any previously set temperature correction
  FastLED.setDither(DISABLE_DITHER); // stops the lights flickering.  see this for explanation: https://github.com/FastLED/FastLED/wiki/FastLED-Temporal-Dithering
  FastLED.setBrightness(BRIGHTNESS); // set the maximum brightness of the whole display
  pinMode(LED_BUILTIN, OUTPUT);
  if (TEST_UI) {
    if (!SERIAL) {
      Serial.begin(9600); // use this on a virtual board in the emulator
    }
  }

    for (uint8_t line = 0; line < NUM_LINES; line++) {
    linesXY[line].x1 = random8() * MATRIX_WIDTH;
    linesXY[line].x2 = random8() * MATRIX_WIDTH;
    linesXY[line].y1 = random8() * MATRIX_HEIGHT;
    linesXY[line].y2 = random8() * MATRIX_HEIGHT;


    if (TEST_UI) {
      Serial.print("linesXY[line].x1: ");
      Serial.print(linesXY[line].x1);
      Serial.print(", linesXY[line].y1: ");
      Serial.print(linesXY[line].y1);
      Serial.print(", linesXY[line].x2: ");
      Serial.print(linesXY[line].x2);
      Serial.print(", linesXY[line].y2: ");
      Serial.print(linesXY[line].y2);
      Serial.print(", line: ");
      Serial.print(line);
      Serial.print(", linesXY[line]: ");
      Serial.println(uint32_t(&linesXY[line]), HEX);
    }
  }
}

void loop() {
  // Draw a line from (7, 1) to (33, 10)
  draw_line(7, 1, 33, 10);
  FastLED.show();
  delay(1000); // Adjust delay time as needed
  fill_solid(leds, NUM_LEDS, CRGB::Black); // Clear the matrix
}

// Function to map 2D (x, y) coordinates to 1D LED array index
int XY(int x, int y) {
  if (y % 2 == 0) {
    // For even rows, return index directly
    return y * MATRIX_WIDTH + x;
  } else {
    // For odd rows, map x to reverse direction
    return y * MATRIX_WIDTH + (MATRIX_WIDTH - 1 - x);
  }
}

// Function to draw a line using Bresenham's line algorithm
void draw_line(int x1, int y1, int x2, int y2) {
  // Iterators, counters required by algorithm
  int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
  // Calculate line deltas
  dx = x2 - x1;
  dy = y2 - y1;
  // Create a positive copy of deltas (makes iterating easier)
  dx1 = abs(dx);
  dy1 = abs(dy);
  // Calculate error intervals for both axis
  px = 2 * dy1 - dx1;
  py = 2 * dx1 - dy1;

  // The line is X-axis dominant
  if (dy1 <= dx1) {
    // Line is drawn left to right
    if (dx >= 0) {
      x = x1; y = y1; xe = x2;
    } else { // Line is drawn right to left (swap ends)
      x = x2; y = y2; xe = x1;
    }
    pixel(x, y); // Draw first pixel
    // Rasterize the line
    for (i = 0; x < xe; i++) {
      x = x + 1;
      // Deal with octants...
      if (px < 0) {
        px = px + 2 * dy1;
      } else {
        if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
          y = y + 1;
        } else {
          y = y - 1;
        }
        px = px + 2 * (dy1 - dx1);
      }
      // Draw pixel from line span at
      // currently rasterized position
      pixel(x, y);
    }
  } else { // The line is Y-axis dominant
    // Line is drawn bottom to top
    if (dy >= 0) {
      x = x1; y = y1; ye = y2;
    } else { // Line is drawn top to bottom
      x = x2; y = y2; ye = y1;
    }
    pixel(x, y); // Draw first pixel
    // Rasterize the line
    for (i = 0; y < ye; i++) {
      y = y + 1;
      // Deal with octants...
      if (py <= 0) {
        py = py + 2 * dx1;
      } else {
        if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
          x = x + 1;
        } else {
          x = x - 1;
        }
        py = py + 2 * (dx1 - dy1);
      }
      // Draw pixel from line span at
      // currently rasterized position
      pixel(x, y);
    }
  }
}

// Function to perform an action with each pixel
void pixel(int x, int y) {
  // Map 2D (x, y) coordinates to 1D LED array index
  int index = XY(x, y);
  // Do something with the pixel at index
  leds[index] = CRGB::White;
}