/* Support of multiple LED layouts. Non-panel version used by SR WLED documentation.

  By: Sutaburosu

  Adapted and testing by: Andrew Tuline

  Original code:

  https://gist.github.com/sutaburosu/43d46240dc2b05683d1e98872885f8db#file-stream_serial-ino-L34-L55
  https://github.com/sutaburosu/LEDpijp/blob/main/XYmatrix.h#L37-L58 (has a fix)


  Other references:

  https://github.com/marcmerlin/ArduinoOnPc-FastLED-GFX-LEDMatrix
  https://github.com/Jorgen-VikingGod/LEDMatrix
  https://macetech.github.io/FastLED-XY-Map-Generator/
  https://github.com/macetech/FastLED-XY-Map-Generator
  https://github.com/marcmerlin/Framebuffer_GFX#custom-xy-mapping-for-irregular-arrays



// Helper functions for a two-dimensional XY matrix of pixels.
//
//     XY(x,y) takes x and y coordinates and returns an LED index number,
//             for use like this:  leds[ XY(x,y) ] == CRGB::Red;
//
// If pixels are driven horizontally at the same end, like below, it's a (ROWMAJOR) layout:
//
//     0 >  1 >  2 >  3 >  4
//                         |
//     .----<----<----<----'
//     |
//     5 >  6 >  7 >  8 >  9
//                         |
//     .----<----<----<----'
//     |
//    10 > 11 > 12 > 13 > 14
//                         |
//     .----<----<----<----'
//     |
//    15 > 16 > 17 > 18 > 19
//
//
// If pixels are driven horizontally and laid out back and forth, it's a (ROWMAJOR | SERPENTINE) layout:
//
//     0 >  1 >  2 >  3 >  4
//                         |
//                         |
//     9 <  8 <  7 <  6 <  5
//     |
//     |
//    10 > 11 > 12 > 13 > 14
//                        |
//                        |
//    19 < 18 < 17 < 16 < 15
//

  We have 5 orientation bits. The values are:

  XY_LAYOUT = SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1

  SERPENTINE - Select if your layout is serpentine. Otherwise, it's not.
  ROWMAJOR   - Select if your layout is horizontal. De-select if it's vertical.
  FLIPMAJOR  - Flip the major axis, ie top to bottom if it's a horizontal layout.
  FLIPMINOR  - Flip the minor axis, ie left to right if it's a horizonal layout.
  TRANSPOSE  - Swap the major and the minor axes (otherwise no swap). Not for general usage.

*/


// Here's a list of definitons to test. --------------------------------------------------------------------------------

#define XY_LAYOUT (ROWMAJOR | SERPENTINE)                // Serpentine with rows at top left. Wokwi standard. Good!
// #define XY_LAYOUT (ROWMAJOR)                             // Non-serpentine with rows at top left. Good!

// #define XY_LAYOUT (ROWMAJOR | SERPENTINE | FLIPMAJOR)    // Serpentine, leds[0] is top right. Good!
// #define XY_LAYOUT (ROWMAJOR | FLIPMAJOR)                 // Non-serpentine, leds[0] is top right. Good!

// #define XY_LAYOUT (ROWMAJOR | SERPENTINE | FLIPMINOR)    // Serpentine, leds[0] is bottom right. Good!
// #define XY_LAYOUT (ROWMAJOR | FLIPMINOR)                 // Non-serpentine, leds[0] is bottom right. Good!

//#define XY_LAYOUT (ROWMAJOR | SERPENTINE | FLIPMAJOR | FLIPMINOR)    // Serpentine, leds[0] is bottom left. Good!
//#define XY_LAYOUT (ROWMAJOR | FLIPMAJOR | FLIPMINOR)                 // Non-serpentine, leds[0] is bottom left. Good!

// #define XY_LAYOUT 0                                      // NOT ROWMAJOR and NOT SERPENTINE. Good!
// #define XY_LAYOUT SERPENTINE                             // Not ROWMAJOR. Good!
// Rest are untested.

// Have NOT tested when TRANSPOSE is set.                   // We'll get there.



#include <FastLED.h>

#define LED_PIN  12

const uint8_t matrixWidth = 16;
const uint8_t matrixHeight = 16;
#define NUM_LEDS matrixWidth * matrixHeight

CRGB leds[NUM_LEDS+1];                       // The +1 is for the out of bounds LED for any out of bound coordinates.

//const bool    matrixSerpentine = true;


enum XY_Layout {
  SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
};


void setup() {
  Serial.begin(115200);
  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(64);
//  dolights();
}


void loop() {
  dolights();
  FastLED.show();
}

void dolights() {

// Raw LED writing to confirm physical layout.
 /*   leds[0] = CRGB::Red;
    leds[1] = CRGB::Green;
    leds[2] = CRGB::Blue;
    leds[31] = CRGB::Orange;
*/ 

// Layout based led's.
  leds[XY(0, 0)] = CRGB::Red;
  leds[XY(1, 0)] = CRGB::Green;
  leds[XY(2, 0)] = CRGB::Blue;
  leds[XY(0, 1)] = CRGB::Orange;

  FastLED.show();
}

// enum XY_Layout {
//   SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
// };

// Advanced XY layout supports multiple orientations.

uint16_t XY(uint8_t x, uint8_t y) {                 // By: Sutaburosu
  uint8_t major, minor, sz_major, sz_minor;
  if (x >= matrixWidth || y >= matrixHeight)
    return NUM_LEDS;
  if (XY_LAYOUT & ROWMAJOR)
    major = x, minor = y, sz_major = matrixWidth,  sz_minor = matrixHeight;
  else
    major = y, minor = x, sz_major = matrixHeight, sz_minor = matrixWidth;
  if (((XY_LAYOUT & FLIPMAJOR) != 0) ^ (((minor & 1) != 0) && ((XY_LAYOUT & SERPENTINE) != 0)))    // A line of magic.
    major = sz_major - 1 - major;
  if (XY_LAYOUT & FLIPMINOR)
    minor = sz_minor - 1 - minor;
  if (XY_LAYOUT & TRANSPOSE)
    return major * (uint16_t) sz_major + minor;
  else
    return minor * (uint16_t) sz_major + major;
}




////////////////////////////////////////////
// OLD AND REFERENCE CODE BELOW HERE.     //
////////////////////////////////////////////

/*
// Basic XY layout doesn't support multiple orientations.

#define XY_LAYOUT (ROWMAJOR | SERPENTINE)  

uint16_t OLD_XY( uint8_t x, uint8_t y)
{
  uint16_t i;

  if( matrixSerpentine == false) {
    i = (y * matrixWidth) + x;
  }

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

  return i;
}
*/



/*

// Advanced XY with debug code.

// enum XY_Layout {
//   SERPENTINE = 16, ROWMAJOR = 8, TRANSPOSE = 4, FLIPMAJOR = 2, FLIPMINOR = 1
// };

uint16_t XY(uint8_t x, uint8_t y) {
  
  uint8_t major, minor, sz_major, sz_minor;

  if (x >= matrixWidth || y >= matrixHeight)
    return NUM_LEDS;                                                             // Failsafe maximum value is one beyond the matrix.

  if (XY_LAYOUT & ROWMAJOR) {
    Serial.println("Rowmajor");
    major = x, minor = y, sz_major = matrixWidth,  sz_minor = matrixHeight;      // The STANDARD layout, leds[0] is top left.
  } else {
    Serial.println("Not Rowmajor");
    major = y, minor = x, sz_major = matrixHeight, sz_minor = matrixWidth;       // Not sure how to set this. Maybe just use a '0'.
  }

  if (((XY_LAYOUT & FLIPMAJOR) != 0) ^ (((minor & 1) != 0) && ((XY_LAYOUT & SERPENTINE) != 0))) {    // A line of magic.
    Serial.println("Flipmajor and Serpentine");
    major = sz_major - 1 - major;
  }

  if (XY_LAYOUT & FLIPMINOR) {
    Serial.println("Flipminor");
    minor = sz_minor - 1 - minor;
  }

  if (XY_LAYOUT & TRANSPOSE) {
    Serial.println("Transposed");
    Serial.println(major *(uint16_t) sz_major + minor);
    return major * (uint16_t) sz_major + minor;
  } else {
    Serial.println("Not Transposed");
    Serial.println(minor * (uint16_t) sz_major + major);
    return minor * (uint16_t) sz_major + major;
  }
}
*/