#include <Arduino.h>
// #include <ArduinoOTA.h>
#include <ArtnetWifi.h>
#include <FastLED.h>
#include <WiFi.h>
// #include <WiFiManager.h>

/* Types and Constants                                                                            */
/*------------------------------------------------------------------------------------------------*/

#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define BRIGHTNESS 15

#define MAX_BAR_NUMBER 2

typedef struct {
  int numLeds;
  uint8_t pin;
} ledBar_t;

ledBar_t ledBarArray[MAX_BAR_NUMBER] = {{.numLeds = 15, .pin = 22}, {.numLeds = 10, .pin = 23}};

#define FOREACH_BARS(FUNC) FUNC(22) FUNC(23)

// LED settings
const int numLeds{165};
// Total number of channels you want to receive (1 led = 3 channels)
CRGB leds[numLeds];
CRGB leds2[15];
CRGB leds3[10];

// Art-Net settings
ArtnetWifi artnet;

/* Global variables                                                                               */
/*------------------------------------------------------------------------------------------------*/
int count = 0;

/* Private function declarations                                                                  */
/*------------------------------------------------------------------------------------------------*/
void init_wifi();
void pride();
void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t *data);
// void setupOTA();

void initTest(CRGB *leds, int numLeds);

/* Pulic function definitions                                                                     */
/*------------------------------------------------------------------------------------------------*/
void setup() {
  delay(3000); // 3 second delay for recovery

  // initialize serial
  Serial.begin(115200);
  Serial.println("Init");

  // initialize wifi
  init_wifi();

  // #define ADD_LEDS_BARS(PIN)                                                                         \
//   FastLED.addLeds<LED_TYPE, PIN, COLOR_ORDER>(leds, 60)                                            \
//       .setCorrection(TypicalLEDStrip)                                                              \
//       .setDither(BRIGHTNESS < 255);
  // FOREACH_BARS(ADD_LEDS_BARS)

  FastLED.addLeds<LED_TYPE, 5, COLOR_ORDER>(leds, numLeds)
      .setCorrection(TypicalLEDStrip)
      .setDither(0);
  // set master brightness control
  FastLED.setBrightness(BRIGHTNESS);
  initTest(leds, numLeds);

  // setupOTA();

  // this will be called for each packet received
  artnet.begin();
  artnet.setArtDmxCallback(onDmxFrame);
}

void loop() {
  // ArduinoOTA.handle();

  artnet.read();

  static uint16_t sLastMillis = millis();
  uint16_t ms = millis();
  uint16_t deltams = ms - sLastMillis;

  if (deltams >= 5000) {
    Serial.print("FPS : ");
    Serial.println(count / 5);
    count = 0;
    sLastMillis = ms;
  }
  ++count;

  FastLED.show();
}

/* Private function definitions                                                                   */
/*------------------------------------------------------------------------------------------------*/

/* Initialize wifi connection */
void init_wifi() {
  IPAddress localIp = IPAddress(192, 168, 1, 78);
  IPAddress gateway = IPAddress(192, 168, 1, 1);
  IPAddress subnet = IPAddress(255, 255, 255, 0);
  // end-block2
  if (!WiFi.config(localIp, gateway, subnet)) {
    Serial.println("STA Failed to configure");
  }
  WiFi.begin("BertheBox", "3615BertheBerthelot");

  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
}

// void setupOTA() {
//   ArduinoOTA
//       .onStart([]() {
//         String type;
//         if (ArduinoOTA.getCommand() == U_FLASH)
//           type = "sketch";
//         else // U_SPIFFS
//           type = "filesystem";
//
//         // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
//         Serial.println("Start updating " + type);
//       })
//       .onEnd([]() { Serial.println("\nEnd"); })
//       .onProgress([](unsigned int progress, unsigned int total) {
//         Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
//       })
//       .onError([](ota_error_t error) {
//         Serial.printf("Error[%u]: ", error);
//         if (error == OTA_AUTH_ERROR)
//           Serial.println("Auth Failed");
//         else if (error == OTA_BEGIN_ERROR)
//           Serial.println("Begin Failed");
//         else if (error == OTA_CONNECT_ERROR)
//           Serial.println("Connect Failed");
//         else if (error == OTA_RECEIVE_ERROR)
//           Serial.println("Receive Failed");
//         else if (error == OTA_END_ERROR)
//           Serial.println("End Failed");
//       });
//
//   ArduinoOTA.begin();
//
//   Serial.println("Ready");
//   Serial.print("IP address: ");
//   Serial.println(WiFi.localIP());
// }

/* initialize led bars */
void setup_ledbars() {}

const int numberOfChannels = numLeds * 3;
const int startUniverse = 0; // CHANGE FOR YOUR SETUP most software this is 1, some software send
                             // out artnet first universe as 0.

// Check if we got all universes
const int maxUniverses = numberOfChannels / 512 + ((numberOfChannels % 512) ? 1 : 0);
bool universesReceived[maxUniverses];
bool sendFrame = 1;
int previousDataLength = 0;

void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t *data) {
  sendFrame = 1;
  // set brightness of the whole strip
  if (universe == 15) {
    FastLED.setBrightness(data[0]);
    FastLED.show();
  }

  // Store which universe has got in
  if ((universe - startUniverse) < maxUniverses) {
    universesReceived[universe - startUniverse] = 1;
  }

  for (int i = 0; i < maxUniverses; i++) {
    if (universesReceived[i] == 0) {
      // Serial.println("Broke");
      sendFrame = 0;
      break;
    }
  }

  // read universe and put into the right part of the display buffer
  for (int i = 0; i < length / 3; i++) {
    int led = i + (universe - startUniverse) * (previousDataLength / 3);
    if (led < numLeds)
      leds[led] = CRGB(data[i * 3], data[i * 3 + 1], data[i * 3 + 2]);
  }
  previousDataLength = length;

  if (sendFrame) {
    FastLED.show();
    // Reset universeReceived to 0
    memset(universesReceived, 0, maxUniverses);
  }
}

void initTest(CRGB *leds, int numLeds) {
  Serial.println("Green");
  for (int i = 0; i < numLeds; i++) {
    leds[i] = CRGB(127, 0, 0);
  }
  FastLED.show();
  delay(500);
  Serial.println("Red");
  for (int i = 0; i < numLeds; i++) {
    leds[i] = CRGB(0, 127, 0);
  }
  FastLED.show();
  delay(500);
  Serial.println("Blue");
  for (int i = 0; i < numLeds; i++) {
    leds[i] = CRGB(0, 0, 127);
  }
  FastLED.show();
  delay(500);
  Serial.println("Done");
  for (int i = 0; i < numLeds; i++) {
    leds[i] = CRGB(0, 0, 0);
  }
  FastLED.show();
}

void pride() {
  static uint16_t sPseudotime = 0;
  static uint16_t sLastMillis = 0;
  static uint16_t sHue16 = 0;

  uint8_t sat8 = beatsin88(87, 220, 250);
  uint8_t brightdepth = beatsin88(341, 96, 224);
  uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256));
  uint8_t msmultiplier = beatsin88(147, 23, 60);

  uint16_t hue16 = sHue16; // gHue * 256;
  uint16_t hueinc16 = beatsin88(113, 1, 3000);

  uint16_t ms = millis();
  uint16_t deltams = ms - sLastMillis;
  sLastMillis = ms;
  sPseudotime += deltams * msmultiplier;
  sHue16 += deltams * beatsin88(400, 5, 9);
  uint16_t brightnesstheta16 = sPseudotime;

  for (uint16_t i = 0; i < numLeds; i++) {
    hue16 += hueinc16;
    uint8_t hue8 = hue16 / 256;

    brightnesstheta16 += brightnessthetainc16;
    uint16_t b16 = sin16(brightnesstheta16) + 32768;

    uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
    uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
    bri8 += (255 - brightdepth);

    CRGB newcolor = CHSV(hue8, sat8, bri8);

    uint16_t pixelnumber = i;
    pixelnumber = (numLeds - 1) - pixelnumber;

    nblend(leds[pixelnumber], newcolor, 64);
  }
}