/*-----------------------------------------------------------------------------------------
 *  This software is in the public domain, furnished "as is", without technical
 *  support, and with no warranty, express or implied, as to its usefulness for
 *  any purpose.
 *
 *  LEDstripe.ino
 *
 *  Functions to controll RGB-LED with an WS2801 stripe.
 *  LED stripes are used to support differnt movement and rotation modes of the ship with
 *  colourscheme and running light.
 *
 *  supported States:
 *  alarm modes LED_Amode[flagnumber]:
 *  - [ 0] Failsafe:          all LED      -> shine red
 *  - [ 1] Wateralert:        LED_stripe_1 -> blinking violet
 *
 *  special modes LED_Smode[flagnumber]:
 *  - [ 0] Turntable rtr:     LED_stripe_1 -> blinking blue for 3 times
 *  - [ 1] Turntable to slow: LED_stripe_1 -> blinking orange
 *  - [ 2] Switchprop rtr:    LED_stripe_2 -> blinking olive for 3 times
 *  - [ 3] SP not calibrated: LED_stripe_2 -> shine olive
 *
 *  movement modes as described in VScontrol (int LED_Mmode):
 *  - [0] MoLess:   LED_stripe_1 -> negativ running light (one LED off) with turntable in yellow
 *                  LED_stripe_2 -> off
 *  - [1] RotOnly:  LED_stripe_1 -> running light with double rotation in colourchange (2 LED)
 *                  LED_stripe_2 -> running light with triple rotation in colourchange (3 LED)
 *  - [2] LinOnly:  LED_Tmode is used
 *
 *  - [2] LinRot:   LED_Tmode is used
 *
 *  movement modes as described in turntable (int LED_Tmode):
 *  - [1] PropMode: LED_stripe_1 -> running light with turntable in green
 *                  LED_stripe_2 -> running light to direction of travel
 *  - [2] ServoMode:LED_stripe_1 -> running light with turntable in blue
 *                  LED_stripe_2 -> negativ running light (one LED off) with turntable in blue
 *  - [3] RotMode:  LED_stripe_1 -> running light with turntable in colourchange
 *                  LED_stripe_2 -> lighting with colourchange
 *  - [4] SyncMode: LED_stripe_1 -> negativ running light (one LED off) with turntable with colourchange
 *                  LED_stripe_2 -> running light to direction of travel with colourchange
 *
 *  Author: Volker Frauenstein
 *  Date: 01/07/2023 Version: 2.7 remapping of LED position in stripes added to reflect
 *                                different instalation position at ship
 *
 *  Date: 04/12/2022 Version: 2.6 interface to Flash LED functions added
 *  Date: 22/04/2021 Version: 2.5 functional implementation of LED modes and LED strip definitions
 *  Date: 04/03/2021 Version: 2.4 changed to WS2801 LED-chip to use SPI interface for LED to resolve
 *                                RTM conflicts
 *  Date: 25/02/2021 Version: 2.3 define of FASTLED_RMT_BUILTIN_DRIVER added to share RTM resources
 *  Date: 13/01/2021 Version: 2.2 LED blinking functionality added
 *  Date: 08/01/2021 Version: 2.1 RGB_LED function for wateralert added
 *  Date: 05/01/2021 Version: 2.0 definitions adapted for ESP32 migration
 *                                without WS2811 interface du to interrupt conficts
 *  Date: 30/12/2020 Version: 1.0 implementation of WS2811 RGB-LED control
 */

// Definitions to handle output to LED driver
//#define FASTLED_RMT_BUILTIN_DRIVER 1// prepare FastLED to share RMT resources with RcPwmRead
//#define FASTLED_FORCE_SOFTWARE_SPI
#include "FastLED.h"                 // include Lib to acces the WS2811 RGB-LED

// definition for pinout to WS2801 LED driver
#define DATA_PIN 13                  // definition of digital data pin for SPI
#define CLOCK_PIN 12                 // definition of digital clock pin for SPI
// global definition of LED stripe behavior
#define BRIGHTNESS 180               // brightness of LED (0 to 255)

// definitions for adjustable values
const int LEDnum = 24;               // number of LED/WS2801 to be controlled
const int stripe1A = 0;              // definition start LED of stripe 1
const int stripe1E = (LEDnum/2) -1;  // definition end LED of stripe 1
const int stripe2A = stripe1E +1;    // definition start LED of stripe 2
const int stripe2E = LEDnum -1;      // definition end LED of stripe 2
const int stripeL = LEDnum/2;        // ammount of LED per stripe
const int Blinks = 3;                // amount of LED blink state changes for LED_Smode blinking time
const uint16_t blinkmilles = 750;    // blinkinterval in ms
// remapping of LED positiom im stripe due to not matching instalation position in the cycle
// mapping starts with first LED (0) at rear of ship (same as at 180 degree)
const int LEDPos[LEDnum] = {6,7,8,9,10,11,0,1,2,3,4,5,18,19,20,21,22,23,12,13,14,15,16,17};

// definition of LED objects
CRGB LEDrgb[LEDnum];

// generig definitions
CRGB rainbow;                        // colour definition of colourchange scheme
int r = 255;                         // inittial definition of rgb color
int g = 0;                           // for colourchange scheme
int b = 0;                           // rainbow
int c_phase = 0;                     // counter to run the colour blend phases
static unsigned long blinkTimer = 0; // timer for blink interval
int counter = 0;                     // counter for MPU startup claibration
bool sblink = true;                  // indicator if state blinking is aktiv
bool bchange = false;                // blinking state change flag
int blinkcountT = 0;                 // counter for Smode turntable blinking
int blinkcountSP = 0;                // counter for Smode SwitchProp blinking

/* definitions transfered to main.h to handle LED state globaly
volatile bool LED_Amode[2] = {false, false};     // alarm LED Mode definitions
volatile bool LED_Smode[4] = {false, false, false, false}; // special LED Mode definitions
volatile int LED_Mmode = 0;                      // movement LED Mode definitions
volatile int LED_Tmode = 0;                      // turntable LED Mode definitions       */

// generig definitions for debug
// #define DEBUG_LED 1
// #define DEBUG_BLINK

void setup_LEDstripes() {            // set up function to enable WS2801

  FastLED.addLeds(LEDrgb, LEDnum).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);

  for (int i = stripe1A; i <= stripe2E; i++) {
    LEDrgb[LEDPos[i]] = CRGB::Yellow;// Startup: all LED -> light yellow
  }
  FastLED.show();
}

void runLEDatStartUp() {             // function to set RGB LED at MPU calibration phase
                                     // one by one with blinkTimer interval
  if (millis() - blinkTimer > blinkmilles) {
    blinkTimer = millis();           // time over, reset timer
    if (counter == 0) {
      for (int i = stripe1A; i <= stripe2E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // switch off all at start or loop
      }
    }
    LEDrgb[LEDPos[counter]] = CRGB::Yellow; // switch on one by one with light yellow
    FastLED.show();
    counter++;                       // prepare for next LED
    if (counter > stripe2E) {        // if all LED on
      counter = 0;}                  // reset counter
    #ifdef DEBUG_S
     Serial.print("counter: ");Serial.println(counter);
    #endif
  }
}
void runLEDstripes() {               // function to set or change colour of RGB LED

  checkBlinkState();                 // check state for blinking LED scheme
  runcolour();                       // update colourchange scheme for this cycle

  // evaluation for movement and turntable LED modes
  switch (LED_Mmode) {               // special movement modes overuling the other movement modes

    case 0:                          // MoLess:   LED_stripe_1 -> negativ running light (one LED off) with turntable in yellow
                                     //           LED_stripe_2 -> off
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Yellow;   // set all LED to yellow of stripe1
      }
      LEDrgb[runLEDTT(true)] = CRGB::Black;

      for (int i = stripe2A; i <= stripe2E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // LEDs off at stillstand for stripe2
      }
      break;

    case 1:                          // RotOnly:  LED_stripe_1 -> running light with double rotation in colourchange (2 LED)
      runLED_Rot2();                 //           LED_stripe_2 -> running light with triple rotation in colourchange (3 LED)
      break;

    default:                         // no special movement mode check other modes

      switch (LED_Tmode) {           // modes as defined with Switchprop settings

      case 1:                        // PropMode: LED_stripe_1 -> running light with turntable in green
        runLED_Prop();               //           LED_stripe_2 -> running light to direction of travel
        break;

      case 2:                        // DirMode:  LED_stripe_1 -> running light with turntable in blue
        runLED_Servo();              //           LED_stripe_2 -> negativ running light (one LED off) with turntable in blue
        break;

      case 3:                        // RotMode:  LED_stripe_1 -> running light with turntable in colourchange
        runLED_Rot();                //           LED_stripe_2 -> lighting with colourchange
        break;

      case 4:                        // SyncMode: LED_stripe_1 -> negativ running light (one LED off) with turntable with colourchange
        runLED_Sync();               //           LED_stripe_2 -> running light to direction of travel with colourchange
        break;
      }
      break;
  }

  // evaluation for special LED modes, overwriting of Mmode setting if needed
  if (LED_Smode[0]) {                // Turntable rtr: LED_stripe_1 -> blinking blue for 3 times
    if (sblink) {                    // check for blinkinterval
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Blue;     // set all LED of stripe1 to blue
      }
      if (bchange) {                 // count amount of blink times
        blinkcountT++;
      }
    }
    else {
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // switch off all LED of stripe1
      }
    }
    if (blinkcountT > Blinks) {      // check if blinking interval is over
      LED_Smode[0] = false;          // reset LED mode
      blinkcountT = 0;               // reset counter
    }
  }

  if (LED_Smode[1]) {                // Turntable to slow: LED_stripe_1 -> blinking orange
    if (sblink) {                    // check for blinkinterval
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Orange;   // set all LED of stripe1 to orange
      }
    }
    else {
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // switch off all LED of stripe1
      }
    }
  }

  if (LED_Smode[2]) {                // Switchprop rtr: LED_stripe_2 -> blinking olive for 3 times
    if (sblink) {                    // check for blinkinterval
      for (int i = stripe2A; i <= stripe2E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Olive;    // set all LED of stripe2 to olive
      }
      if (bchange) {                 // count amount of blink times
        blinkcountSP++;
      }
    }
    else {
      for (int i = stripe2A; i <= stripe2E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // switch off all LED of stripe2
      }
    }
    if (blinkcountSP > Blinks) {     // check if blinking interval is over
      LED_Smode[2] = false;          // reset LED mode
      blinkcountSP = 0;              // reset counter
    }
  }

  if (LED_Smode[3]) {                // SP not calibrated: LED_stripe_2 -> light olive
    for (int i = stripe2A; i <= stripe2E; i++) {
      LEDrgb[LEDPos[i]] = CRGB::Olive;      // set all LED of stripe2 to olive
    }
  }

  // evaluation for alarm LED modes, overwriting of Mmode and Smode settings if needed
  if (LED_Amode[0]) {                // Failsafe: all LED -> light red
    for (int i = stripe1A; i <= stripe2E; i++) {
      LEDrgb[LEDPos[i]] = CRGB::Red;        // set all LED of stripe1 and stripe2 to red
    }
  }

  if (LED_Amode[1]) {                // Wateralert: LED_stripe_1 -> blinking violet
    if (sblink) {                    // check for blinkinterval
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::MediumPurple;// set all LED of stripe1 to violet
      }
    }
    else {
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // switch off all LED of stripe1
      }
    }
  }

  FastLED.show();                    // send evaluation for this cycle to LEDs

  #ifdef DEBUG_LED
   Serial.print(" LED_Mmode: ");Serial.print(LED_Mmode);
   Serial.print(" LED_Smode: [");
   for (int i=0; i < 3; i++){
    Serial.print(LED_Smode[i]);Serial.print(",");
   }
   Serial.print(LED_Smode[3]);Serial.print("] LED_Amode: [");
   Serial.print(LED_Amode[0]);Serial.print(",");Serial.print(LED_Amode[1]);
   Serial.print("] sblink: ");Serial.print(sblink);
   Serial.print(", bchange: ");Serial.print(bchange);
   Serial.print(", bcount: [T");Serial.print(blinkcountT);Serial.print(",SP");Serial.print(blinkcountSP);Serial.print("]");
   Serial.print(", cPhase: ");Serial.print(c_phase);
   //Serial.print(", rainbow: [r");Serial.print(r);Serial.print(",g");Serial.print(g);
   //Serial.print(",b");Serial.print(b);Serial.println("]");
   Serial.print(", stepOffset: ");Serial.println(stepOffset);
  #endif
}                                    // function set_LEDstate closed

void runLED_Prop() {                 // PropMode: LED_stripe_1 -> running light with turntable in green
                                     //           LED_stripe_2 -> running light to direction of travel
  LEDrgb[runLEDTT(true)] = CRGB::Green;  // stripe1 -> set one LED to colour
  LEDrgb[runLEDir(true)] = CRGB::Green;  // stripe2 -> set one LED to colour
}

void runLED_Servo() {                // DirMode:  LED_stripe_1 -> running light with turntable in blue
                                     //           LED_stripe_2 -> negativ running light (one LED off) with turntable in blue
  LEDrgb[runLEDTT(true)] = CRGB::Blue;   // stripe1 -> set one LED to colour
  for (int i = stripe2A; i <= stripe2E; i++) {
    LEDrgb[LEDPos[i]] = CRGB::Blue;      // set all LED to blue of stripe2
  }
  LEDrgb[runLEDir(false)] = CRGB::Black; // stripe2 -> set the one LED off
}

void runLED_Rot() {                  // RotMode:  LED_stripe_1 -> running light with turntable in colourchange
                                     //           LED_stripe_2 -> lighting with colourchange
//  runLEDTT (rainbow);              // not working with new verion
  LEDrgb[runLEDTT(true)] = rainbow;  // stripe1 -> set one LED to colour
  for (int i = stripe2A; i <= stripe2E; i++) {
    LEDrgb[LEDPos[i]] = rainbow;     // set all LED to colour of stripe2
  }
}

void runLED_Sync() {                 // SyncMode: LED_stripe_1 -> negativ running light (one LED off) with turntable with colourchange
                                     //           LED_stripe_2 -> running light to direction of travel with colourchange
  for (int i = stripe1A; i <= stripe1E; i++) {
    LEDrgb[LEDPos[i]] = CRGB::Green;        // set all LED to green
  }
  for (int i = stripe2A; i <= stripe2E; i++) {
    LEDrgb[LEDPos[i]] = CRGB::Blue;         // set all LED to blue
  }
}

void runLED_Rot2() {                 // RotOnly:  LED_stripe_1 -> running light with double rotation in colourchange (2 LED)
                                     //           LED_stripe_2 -> running light with triple rotation in colourchange (3 LED)

  if (abs(yawrate) > 720) {          // fast rotation: LED_stripe_1 -> blinking orange
    if (sblink) {                    // check for blinkinterval
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::White;    // set all LED of stripe1 to orange
      }
    }
    else {
      for (int i = stripe1A; i <= stripe1E; i++) {
        LEDrgb[LEDPos[i]] = CRGB::Black;    // switch off all LED of stripe1
      }
    }
  }
  else {
    for (int i = stripe1A; i <= stripe2E; i++) {
      LEDrgb[LEDPos[i]] = CRGB::White;      // set all LED to white
    }
  }
}

void checkBlinkState() {             // function to switch blinking state

  if (millis() - blinkTimer > blinkmilles) {
    blinkTimer = millis();           // time over, reset timer
    bchange = true;                  // update blinking state change flag
    sblink = !sblink;                // set semaphore to other state for LED
    #ifdef DEBUG_BLINK
      Serial.print(" Blink reset,");
    #endif
    }
  else {bchange = false;}            // reset blinking state change flag
}

int runLEDTT(bool kill) {            // function to run LED light with turntable
                                     // for Stripe 1 only
  // find turntable position as LED number in stripe
  int flash = LEDPos[tablePos(stripeL)];

  if (kill) {                        // check for switch off LEDs
    for (int i = stripe1A; i <= stripe1E; i++) {
      LEDrgb[i] = CRGB::Black;       // switch off all LED of stripe1
    }
  }
  return flash;                      // return result
}

int runLEDir(bool kill) {            // function to run LED light with direction of travel
                                     // for Stripe 2 only
  float tseg = calcYawPos()/stripeL; // find yaw position as segment LED number in stripe
  int flash = (int)(tseg + 0.5);     // transfer segment LED number to int and round it
  flash = LEDPos[flash + stripeL];   // map it to LED position in Stripe 2

  if (kill) {
    for (int i = stripe2A; i <= stripe2E; i++) {
      LEDrgb[i] = CRGB::Black;      // switch off all LED of stripe2
    }
  }
  return flash;                      // return result
}

void runcolour() {                   // function to cycle thrue the colour scheme

  switch (c_phase) {

    case 0:                          // colour phase from red
      b++;                           // to magenta
      if (b > 254) {c_phase++;}      // r255, g000, b255
      break;

    case 1:                          // colour phase from magenta
      r--;                           // to blue
      if (r < 1) {c_phase++;}        // r000, g000, b255
      break;

    case 2:                          // colour phase from blue
      g++;                           // to cyan
      if (g > 254) {c_phase++;}      // r000, g255, b255
      break;

    case 3:                          // colour phase from cyan
      b--;                           // to green
      if (b < 1) {c_phase++;}        // r000, g255, b000
      break;

    case 4:                          // colour phase from green
      r++;                           // to yellow
      if (r > 254) {c_phase++;}      // r255, g255, b000
      break;

    case 5:                          // colour phase from cyan
      g--;                           // to green
      if (g < 1) {c_phase = 0;}      // r255, g000, b000 -> restart cycle
      break;
  }
  rainbow = CRGB(r,g,b);
}                                    // end function runcolour