// https://forum.arduino.cc/t/flag-led-matrix/1180853/

#include <FastLED.h>
#include <avr/pgmspace.h> // for PROGMEM

#define ROWS 16 // rows of Neopixels
#define COLS 32 // columns per row
#define NUMPIX (ROWS * COLS)
#define MATRIX_PIN 2
#define MAXBRIGHT 255
int INTENSITY[] = {255, 127, 63}; // RIDGE (bright), NODE (middle), TROUGH (dim)

CRGB led[NUMPIX];

const PROGMEM char flagUS[] = { // sizeof() = 257
  "________________" // 0 - off
  "#-#-#-#@@@@@@@@@" // 1 - blu/wht/red
  "-#-#-#----------" // 2
  "#-#-#-#@@@@@@@@@" // 3
  "-#-#-#----------" // 4
  "#-#-#-#@@@@@@@@@" // 5
  "-#-#-#----------" // 6
  "#-#-#-#@@@@@@@@@" // 7
  "----------------" // 8 - wht
  "@@@@@@@@@@@@@@@@" // 9 - red
  "----------------" // 10
  "@@@@@@@@@@@@@@@@" // 11
  "----------------" // 12
  "@@@@@@@@@@@@@@@@" // 13
  "________________" // 14
  "________________" // 15
};

const PROGMEM char poppy[] = {
  "%_______________" // 0 - off / grn test
  "______@@@@______" // 1 - red
  "____@@@@@@@@____" // 2
  "___&@@@@@@@@&___" // 3
  "__@@&@@@@@@&@@__" // 4
  "__@@@&@$$@&@@@__" // 5 - red/dark
  "_@@@@@$$$$@@@@@_" // 6
  "_@@@@@$$$$@@@@@_" // 7
  "_@@@@@$$$$@@@@@_" // 8
  "__@@@&@$$@&@@@__" // 9
  "__@@&@@@@@@&@@__" // 10
  "___&@@@@@@@@&___" // 11
  "____@@@@@@@@____" // 12
  "______@@@@______" // 13
  "________________" // 14
  "_______________%" // 15 - grn test
};

void setup() {
  FastLED.addLeds<WS2812B, MATRIX_PIN, GRB>(led, NUMPIX);
  FastLED.setBrightness(MAXBRIGHT); // Adjust the brightness value as needed
  FastLED.clear(); // clear pixel buffer
  FastLED.show(); // display cleared buffer
  randomSeed(analogRead(A0)); // increase randomness - not needed
  Serial.begin(9600); // debugging - not needed

  // These functions are stand-alone.

  showFlags(); // in work - combining showIS and showUS
  // waveFlags(); // in work - only changing one column takes a long time lighting.
  // showUS(); // show US flag
  // showIS(); // show IS flag
  // flagcorners(); // flag corners
  // fourcorners(); // pixel matrix corners
  // serial(); // output PROGMEM to serial monitor
  serialSideBySide(); // output PROGMEM to serial monitor side by side

  FastLED.show(); // last function called for pixels to be displayed
}

void loop() {
  // randomPixels(); FastLED.show(); // random pixel in random color
}

void showFlags() { // show both flags
  int asciiChar; // the data read from PROGMEM
  for (int flag = 0; flag < 2; flag++) { // 0 = US, 1 = IS
    int row = 0, column = 0;
    for (int i = 0; i < 257; i++) { // strlen(flagXX) = 257
      if (!flag) // select which flag to display
        asciiChar = pgm_read_byte_near(flagUS + i); // flagXX is a contiguous line of text
      else
        asciiChar = pgm_read_byte_near(poppy + i);
      if (asciiChar == '-')
        led[(15 - row) * COLS + column + flag * 16] =  CRGB(INTENSITY[1], INTENSITY[1], INTENSITY[1]); // WHITE
      if (asciiChar == '#')
        led[(15 - row) * COLS + column + flag * 16] =  CRGB(0, 0, INTENSITY[1]); // BLUE
      if (asciiChar == '@')
        led[(15 - row) * COLS + column + flag * 16] =  CRGB(INTENSITY[1], 0, 0); // RED
      if (asciiChar == '&')
        led[(15 - row) * COLS + column + flag * 16] =  CRGB(15, 0, 0); // "light" red
      if (asciiChar == '$')
        led[(15 - row) * COLS + column + flag * 16] =  CRGB(0, 0, 15); // "dark" blue
      if (asciiChar == '%') { // test character
        led[(15 - row) * COLS + column + flag * 16] =  CRGB(0, 255, 0); // GREEN - test % placement
        Serial.print("grn @ i:"); Serial.print(i); Serial.print(" ");
        Serial.print("row:"); Serial.print(row); Serial.print(" ");
        Serial.print("col:"); Serial.println(column);
      }
      column++; // move right one column
      if (column % 16 == 0) { // indicates 16 columns have been counted
        row++; // at the end of a column, move down one row
        column = 0; // at the end of a column, reset to first column
      }
    }
  }
}

void showUS() { // US flag in neopixels
  int column = 0, row = 0, flag = 0;
  flag = flag * 16; // 0 = US, 16 = IS
  for (int i = 0; i < strlen(flagUS); i++) {
    int asciiChar = pgm_read_byte_near(flagUS + i);
    if (asciiChar == '-') // white
      led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[1], INTENSITY[1], INTENSITY[1]); // WHITE
    if (asciiChar == '#') // blue
      led[(15 - row) * COLS + column + flag] =  CRGB(0, 0, INTENSITY[1]); // BLUE
    if (asciiChar == '@') // red
      led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[1], 0, 0); // RED
    if (asciiChar == '%') { // green
      led[(15 - row) * COLS + column + flag] =  CRGB(0, 255, 0); // testi % placement
      Serial.print("i:"); Serial.print(i); Serial.print(" ");
      Serial.print("row:"); Serial.print(row); Serial.print(" ");
      Serial.print("col:"); Serial.println(column);
    }
    column++;
    if (column % 16 == 0) {
      row++;
      column = 0;
    }
  }
}

void showIS() { // IS flag in neopixels
  int column = 0, row = 0, flag = 1;
  flag = flag * 16; // 0 = US, 16 = IS
  for (int i = 0; i < strlen(poppy); i++) {
    int asciiChar = pgm_read_byte_near(poppy + i);
    if (asciiChar == '-')
      led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[1], INTENSITY[1], INTENSITY[1]); // WHITE
    if (asciiChar == '#')
      led[(15 - row) * COLS + column + flag] =  CRGB(0, 0, INTENSITY[1]); // BLUE
    if (asciiChar == '@')
      led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[1], 0, 0); // RED
    if (asciiChar == '%') { // test character
      led[(15 - row) * COLS + column + flag] =  CRGB(0, 255, 0); // GREEN - for testing where % is placed
      Serial.print("i:"); Serial.print(i); Serial.print(" ");
      Serial.print("row:"); Serial.print(row); Serial.print(" ");
      Serial.print("col:"); Serial.println(column);
    }
    column++;
    if (column % 16 == 0) {
      row++;
      column = 0;
    }
  }
}

void waveFlags() { // change intensity in columns for waving flag
  int wave = 0, column = 0, row = 0, flag = 0;
  flag = flag * 16; // 0 = US, 16 = IS

  showUS(); FastLED.show();  // display flag

  delay(500);  // Pause

  for (int i = 0; i < strlen(flagUS); i++) {  // read through PROGMEM
    if (i % 16 == 4) {    // identify the column
      int asciiChar = pgm_read_byte_near(flagUS + i);

      // change intensity to high
      if (asciiChar == '-')      // WHITE - Compare character to color
        led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[2], INTENSITY[2], INTENSITY[2]); // set pixel intensity
      if (asciiChar == '#')
        led[(15 - row) * COLS + column + flag] =  CRGB(0, 0, INTENSITY[2]); // BLUE
      if (asciiChar == '@')
        led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[2], 0, 0); // RED
      if (asciiChar == '%') { // test character
        led[(15 - row) * COLS + column + flag] =  CRGB(0, 255, 0); // GREEN - for testing where % is placed
        Serial.print(i); Serial.print(" "); Serial.print(row); Serial.print(" "); Serial.println(column);
      }
      FastLED.show(); delay(500);

      // change intensity to middle
      if (asciiChar == '-')      // WHITE - Compare character to color
        led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[1], INTENSITY[1], INTENSITY[1]); // set pixel intensity
      if (asciiChar == '#')
        led[(15 - row) * COLS + column + flag] =  CRGB(0, 0, INTENSITY[1]); // BLUE
      if (asciiChar == '@')
        led[(15 - row) * COLS + column + flag] =  CRGB(INTENSITY[1], 0, 0); // RED
      if (asciiChar == '%') { // test character
        led[(15 - row) * COLS + column + flag] =  CRGB(0, 255, 0); // GREEN - for testing where % is placed
        Serial.print(i); Serial.print(" "); Serial.print(row); Serial.print(" "); Serial.println(column);
      }
      FastLED.show(); delay(500);
    }

    column++;
    if (column % 16 == 0) {
      row++;
      column = 0;
    }
  }

  // TO DO on flagWave()
  // Read column X (column pixels increas by 16: 0, 16, 32, 48, ... 240)
  // Set cX to ridge
  // Hard-code the SECOND column to 191 (int node = 191).
  // Find the pattern in the pseudo-code pseudo-formula (above)
  // Use the formula to loop the FIRST column from "trough" to "node" to "ridge" to "node"
  // Code a pixel step from column 1 to column 32 on row 0 (from 0 to 31)
  // Code a pixel step from column 2 to column 32 on row 1 (this one goes from 64 to 32)
  // Make them both move.
  // Use the "wave" on 8 and 9.
}

void serial() { // show flags in serial monitor
  for (int i = 0; i < strlen(flagUS); i++) {
    if (i % 16 == 0)
      Serial.println();
    int us = pgm_read_byte_near(flagUS + i);
    Serial.write(us);
  }
  for (int i = 0; i < strlen(poppy); i++) {
    if (i % 16 == 0)
      Serial.println();
    int is = pgm_read_byte_near(poppy + i);
    Serial.write(is);
  }
}

void serialSideBySide() { // show flags in serial monitor, side by side
  for (int i = 0; i < 16; i++) {
    for (int j = 0; j < 16; j++) {
      Serial.write(pgm_read_byte_near(flagUS + j + i * 16));
    }
    Serial.print(" ");
    for (int j = 0; j < 16; j++) {
      Serial.write(pgm_read_byte_near(poppy + j + i * 16));
    }
    Serial.println();
  }
}

/*
    15*32+0+f               15*32+31+f
    led[480]                  led[511]
    +--------------------------------+
    |15                              |
    |                                |
    | PROGMEM ASCII to PIXEL         |
    | PIX = row * COLS + col + FLAG  |
    |                                |
    +0_____________________________31+
    led[0]                     led[31]
    0 * 32 + 0             0 * 32 + 31
*/

void flagcorners() { // show the four corners of the neopixels
  led[ 0 * 32 +  0] =  CRGB(255, 0, 255); // first flag, first row, first column
  led[ 0 * 32 + 15] =  CRGB(255, 128, 0); // first flag, first row, last column
  led[ 0 * 32 + 16] =  CRGB(255, 255, 0); // second flag, first row, first column
  led[ 0 * 32 + 31] =  CRGB(0, 255, 255); // second flag, first row, last column
  led[15 * 32 +  0] =  CRGB(255, 0, 255); // first flag, last row, first column
  led[15 * 32 + 15] =  CRGB(255, 128, 0); // first flag, last row, last column
  led[15 * 32 + 16] =  CRGB(255, 255, 0); // second flag, last row, first column
  led[15 * 32 + 31] =  CRGB(0, 255, 255); // second flag, last row, last column
}

void fourcorners() {
  led[0]   =  CRGB(255, 000, 000); // bottom, left
  led[31]  =  CRGB(000, 000, 255); // bottom, right
  led[480] =  CRGB(255, 255, 000); // top, left
  led[511] =  CRGB(000, 255, 000); // top, right
}

void randomPixels() { // random pixel in random PRIMARY and SECONDARY color on ROWS x COLS matrix
  led[random(ROWS) * random(COLS)] = CRGB((random(2) * 255), (random(2) * 255), (random(2) * 255));
}