/*-----------------------------------------------------------------------------------------
 *  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.
 *
 *  FlashLED.ino
 *
 *  Function to time 4 high power LEDs during rotation of the ship.
 *  The LEDs are 60 degrees separeted from each other alonge the round body of the ship.
 *  The 0 and 6 o'clock spaces are reserved for Loudspeaker and Fog-function.
 *  The modes are triggered with the two switches of Switchprop switchOn[S0,S1] and
 *  combinated with the turntable modes.
 *  PropMode [0,0]: FlashLEDs run against turntable with same rotation speed -> PropFlashLED
 *                  Single FlashLED will flash as long turntable is in that sector (1/12 of full rotation interval)
 *  ServoMode[0,1]: FlashLEDs runs clock or anticlock wise matching turntable deflection. Flash cycle get faster as
 *                  delta position of turntable front to centerposition rises -> ServoFlashLED
 *  RotMode  [1,0]: FlashLEDs are triggered if turntable front is in line with it.
 *                  The Flash Duration is shorter with higher turntable rotation speeds.
 *                  If ship is not rotating no LED will be flashed -> SyncFlashLED
 *  SyncMode [1,1]: FlashLEDs are triggered if turntable front is in line with it.
 *                  The Flash Duration is shorter with higher turntable rotation speeds.
 *                  If ship is not rotating no LED will be flashed -> SyncFlashLED
 *  Function calls of FlashLED are consolidated within Turntable.ino runTable() function.
 *
 *  Author: Volker Frauenstein
 *  Date: 13/01/2023  Version: 1.2 implementation of functions for SyncFlashLED and ServoFlashLED
 *
 *  Date: 09/01/2023  Version: 1.1 implementation of interface to Mosfett swiches and function PropFlashLED
 *  Date: 04/12/2022  Version: 1.0 implementation of interface to retais card
 *
 */

// pinout definition for the relais to trigger the 4 Flash LEDs
const int Flash1 = 32;                     // control pin for Flash LED on 2 o'clock
const int Flash2 = 33;                     // control pin for Flash LED on 4 o'clock
const int Flash4 = 25;                     // control pin for Flash LED on 8 o'clock
const int Flash5 = 27;                     // control pin for Flash LED on 10 o'clock

// generig definitions for debug
//#define DEBUG_F 1                          // set activ if debuging of Flash function is needed

// definitions for adjustable values
int FlashDurFactor = 2;                    // factor for flash duration in times of 20ms (e.g 3 -> flash min 60ms / max 240ms )
const float dzServo = 0.1;                 // Deadzone of x% positiv values of ServoFlashLED function
const float dzNServo = - 0.1;              // RC Channel Deadzone of x% negativ values of ServoFlashLED function

// generig definitions
const int FlashNum = 5;                                     // number of FlashLEDs excluding position 12 o'clock
const int FlashPosMid[FlashNum] = {33, 67, 100, 133, 167};  // Position of FlashLEDs as stepposition of Turntable
const int FlashPosStart[FlashNum] = {17, 51, 84, 116, 151}; // Position of FlashLEDs area start as stepposition of Turntable
const int FlashPosEnd[FlashNum]  = {50, 83, 115, 150, 183}; // Position of FlashLEDs area end as stepposition of Turntable
const int FlashPos[FlashNum] = {4, 3, 2, 1, 0};             // array to replace rotating position for PropFlashLED funktion
bool FlashOn[FlashNum] = {false,false,false,false,false};   // flag to mark if single LED is on
uint8_t FlashRunning = 0;   							 // counter for running flashes in 20ms intervals
int VOffset = 0;                           // virtuel stepOffset

void setup_FlashLED() {                    // set up function to attach the relais / LEDs to I/O pin
 pinMode(Flash1, OUTPUT);
 pinMode(Flash2, OUTPUT);
 pinMode(Flash4, OUTPUT);
 pinMode(Flash5, OUTPUT);
 failsafe_FlashLED();                      // and switch off all relais / LEDs
}

void failsafe_FlashLED() {                 // set all relais / LEDs to to failsafe state
 digitalWrite(Flash1, LOW);
 digitalWrite(Flash2, LOW);
 digitalWrite(Flash4, LOW);
 digitalWrite(Flash5, LOW);
}

// main functions for modes of the 4 FlashLEDs

void PropFlashLED(int Steps) {             // function to coordinate FlashLED switching oposite of turntable position

  int TempPos = stepOffset + (Steps/2);    // turntable position at midterm of this PWM cycle
  uint8_t i;                               // counter for loop
  bool Sector0 = true;                     // flag to mark sector with stepcounter overrun

  if (TempPos < 0) {                       // turtable will cross startposition
    TempPos = TempPos + 200;
  }                                        // no else needed
  #ifdef DEBUG_F
     int j;
     Serial.print("TempPos: ");Serial.print(TempPos);Serial.print(", ");
  #endif
  for (i = 0; i < FlashNum; i++) {         // run over the FlasLED position and check if we are inside
                                           // i=0 is turntable on FlashLED position 2 o'clock
    if (constrain(TempPos, FlashPosStart[i], FlashPosEnd[i])== TempPos) {
      Sector0 = false;                     // sector found other than 12 o'clock
      if (FlashOn[FlashPos[i]]) {          // check if still in same sector and FlashLED is already on
        #ifdef DEBUG_F
          Serial.print("FlashOn[");Serial.print(FlashPos[i]);Serial.print("] on, ");
        #endif
        return;
      }
      else {                               // turntable has reached next FlashLED area
        switch (i) {                       // switch of last FlashLED and switch on new one

          case 0:                          // turntable is on 2 o'clock position,
            digitalWrite(Flash4, LOW );    // if turntable goes anticlockwise we should
            FlashOn[FlashPos[i+1]] = false;// swicht of FlashLED on 8 o'clock and set flag
            digitalWrite(Flash5, HIGH);    // switch on FlashLED on 10 o'clock position
            FlashOn[FlashPos[i]] = true;   // and set flag
            #ifdef DEBUG_F
              Serial.print("FlashPos[");Serial.print(FlashPos[i]);Serial.print("], ");
              Serial.print("FlashOn[");Serial.print(FlashOn[0]);
              for (j = 1;j < FlashNum; j++) {
                Serial.print(", ");Serial.print(FlashOn[j]);
              }
              Serial.print("], ");
              Serial.println("F5on (F4off), ");
            #endif
            break;

          case 1:                          // turntable is on 4 o'clock position,
            digitalWrite(Flash5, LOW);     // if turntable goes clockwise we should
            FlashOn[FlashPos[i-1]] = false;// swicht of FlashLED on 10 o'clock and set flag
            digitalWrite(Flash4, HIGH);    // switch on FlashLED on 8 o'clock position
            FlashOn[FlashPos[i]] = true;   // and set flag
            #ifdef DEBUG_F
              Serial.print("FlashPos[");Serial.print(FlashPos[i]);Serial.print("], ");
              Serial.print("FlashOn[");Serial.print(FlashOn[0]);
              for (j = 1;j < FlashNum; j++) {
                Serial.print(", ");Serial.print(FlashOn[j]);
              }
              Serial.print("], ");
              Serial.println("F4on (F5off), ");
            #endif
            break;

          case 2:                          // turntable is on 6 o'clock position,
            digitalWrite(Flash2, LOW);     // switch off FlashLED on 4 o'clock position
            FlashOn[FlashPos[i+1]] = false;// for anticlockwise turntable rotation and set flag
            digitalWrite(Flash4, LOW);     // switch off FlashLED on 8 o'clock position
            FlashOn[FlashPos[i-1]] = false;// for clockwise turntable rotation andset flag
            #ifdef DEBUG_F
              Serial.print("FlashPos[");Serial.print(FlashPos[i]);Serial.print("], ");
              Serial.print("FlashOn[");Serial.print(FlashOn[0]);
              for (j = 1;j < FlashNum; j++) {
                Serial.print(", ");Serial.print(FlashOn[j]);
              }
              Serial.print("], ");
              Serial.println("(F2+F4off), ");
            #endif
            break;                         // no FlashLED on 6 o'clock position

          case 3:                          // turntable is on 8 o'clock position,
            digitalWrite(Flash1, LOW);     // switch on FlashLED on 2 o'clock position
            FlashOn[FlashPos[i+1]] = false;// for anticlockwise turntable rotation and set flag
            digitalWrite(Flash2, HIGH);    // switch on FlashLED on 4 o'clock position
            FlashOn[FlashPos[i]] = true;   // set flag
            #ifdef DEBUG_F
              Serial.print("FlashPos[");Serial.print(FlashPos[i]);Serial.print("], ");
              Serial.print("FlashOn[");Serial.print(FlashOn[0]);
              for (j = 1;j < FlashNum; j++) {
                Serial.print(", ");Serial.print(FlashOn[j]);
              }
              Serial.print("], ");
              Serial.println("F2on (F1off), ");
            #endif
            break;

          case 4:                          // turntable is on 10 o'clock position,
            digitalWrite(Flash2, LOW);     // switch off FlashLED on 4 o'clock position
            FlashOn[FlashPos[i-1]] = false;// for clockwise turntable rotation and set flag
            digitalWrite(Flash1, HIGH);    // switch on FlashLED on 2 o'clock position
            FlashOn[FlashPos[i]] = true;   // set flag
            #ifdef DEBUG_F
              Serial.print("FlashPos[");Serial.print(FlashPos[i]);Serial.print("], ");
              Serial.print("FlashOn[");Serial.print(FlashOn[0]);
              for (j = 1;j < FlashNum; j++) {
                Serial.print(", ");Serial.print(FlashOn[j]);
              }
              Serial.print("], ");
              Serial.println("F1on (F2off), ");
            #endif
            break;
        }                                  // end switch case
      }                                    // end else alway returned via break
    }
  }                                        // end for loop
                                           // turntable is on 12 o'clock position,
  if (Sector0) {                           // no FlashLED on 12 o'clock position
    digitalWrite(Flash1, LOW);             // switch off FlashLED on 2 o'clock position
    FlashOn[FlashPos[4]] = false;          // for anticlockwise turntable rotation and set flag
    digitalWrite(Flash5, LOW);             // switch off FlashLED on 10 o'clock position
    FlashOn[FlashPos[0]] = false;          // for clockwise turntable rotation and set flag
    #ifdef DEBUG_F
      Serial.print("FlashPos[");Serial.print(FlashPos[i]);Serial.print("], ");
      Serial.print("FlashOn[");Serial.print(FlashOn[0]);
      for (j = 1;j < FlashNum; j++) {
        Serial.print(", ");Serial.print(FlashOn[j]);
      }
      Serial.print("], ");
      Serial.println("(F1+F5off), ");
    #endif
  }
}                                          // function PropFlashLED closed

void ServoFlashLED(float PosServo){        // function to coordinate FlashLEDs with turntable position in servo mode

  if ((PosServo > dzNServo) && (PosServo < dzServo)) {
    return;                                // check if Turntable in deadzone and nop
  }

  int Vs = PosServo*10;                    // set value for flash cycle duration as virtual deltasteps

  if (!CheckFlashTime()) {                 // check if one Flash is on
                                           // no,
    RunFlashTime(VOffset + (Vs/2), Vs);    // prepare checks and switch FlashLEDs
  }                                        // if CheckFlashTime end, no else needed for FlashRunning update
  VOffset = VOffset + Vs;
  if (VOffset < 0) VOffset = VOffset + 200;
  if (VOffset > 199) VOffset = VOffset - 200;
  //SyncFlashLED(Vsteps);
  #ifdef DEBUG_F
    Serial.print("nop, FlashServo :");Serial.println(FlashRunning);
  #endif
}                                          // function ServoFlashLED closed

void SyncFlashLED(int dS){                 // function to coordinate FlashLEDs with turntable position in Rot and Sync mode

  if (LED_Mmode == 0 || LED_Mmode == 2) {  // do nothing if we are in Moless or LinOnly mode within
    #ifdef DEBUG_F
        Serial.print("nop Mmode, ");
    #endif
    return;                                // return without FlashLED use
  }

  if (!CheckFlashTime()) {                 // check if one Flash is on
                                           // no,
    RunFlashTime(stepOffset + (dS/2), dS); // prepare checks and switch FlashLEDs
  }                                        // if CheckFlashTime end, no else needed for FlashRunning update
  #ifdef DEBUG_F
    Serial.print("nop, FlashSync :");Serial.println(FlashRunning);
  #endif
}                                          // function SyncFlashLED closed

bool CheckFlashTime() {                    // function to check if flash is

  uint8_t i;                               // counter for loop

  if (FlashRunning > 0) {                  // check if one flash is aready flashing
    if (FlashRunning > 1) {                // check if Flash must be shut off this cycle
      FlashRunning--;                      // no - just count down
      return true;
    }
    else {
      for (i = 0; i < FlashNum; i++) {      // yes find Flash to shutoff
        if (FlashOn[i]) {
          switch (i) {                        // switch of activ FlashLED

           case 0:
             digitalWrite(Flash1, LOW);     // switch off FlashLED on 2 o'clock position
             FlashOn[i] = false;            // and switch off flag as well
             break;

           case 1:
             digitalWrite(Flash2, LOW);     // switch off FlashLED on 4 o'clock position
             FlashOn[i] = false;            // and switch off flag as well
             break;

           case 2:
             #ifdef DEBUG_F
               Serial.println("Error: CheckFlashTime case 2 should not happen!");
               Serial.print("FlashRunning: ");Serial.print(FlashRunning);
               Serial.print(", i:");Serial.println(i);
             #endif
             break;

           case 3:
             digitalWrite(Flash4, LOW);     // switch off FlashLED on 8 o'clock position
             FlashOn[i] = false;            // and switch off flag as well
             break;

           case 4:
             digitalWrite(Flash5, LOW);     // switch off FlashLED on 10 o'clock position
             FlashOn[i] = false;            // and switch off flag as well
             break;
          }                                 // switch case end
        }
      }
      FlashRunning = 0;                     // Flash is off, Timer to zero
      return false;                         // Return
    }                                       // Else of FlashRunning > 1 End
  }                                         // if FlashRunning > 0 End
  else return false;                        // No Flash was on , just Return
}                                           // function CheckFlashTime closed

void RunFlashTime(int MidPos, int DeltaS) {

  if (MidPos > 199) {                       // turntable will cross startposition clock wise
    MidPos = MidPos - 200;                  // reset SteppCounter
  }                                         // no else needed

  if (MidPos < 0) {                         // turnable will cross startposition anti clock wise
    MidPos = MidPos + 200;                  // reset SteppCounter
  }

  int z = 1 + abs(DeltaS)/2;                // calculate boundary of position (higher stepspeed -> larger boundary)
  uint8_t i;
  for (i = 0; i < FlashNum; i++) {          // run over the FlasLED positions and check if turntable is on this position

    if (constrain(MidPos, FlashPosMid[i]-z, FlashPosMid[i]+z)== MidPos) {
      switch (i) {                          // found match for position

        case 0:
          digitalWrite(Flash1, HIGH);       // switch on FlashLED on 2 o'clock position,
          FlashOn[i] = true;                // set flag and flash duration according to actuel stepspeedand
          FlashRunning = CalcFlashDuration(z);
          #ifdef DEBUG_F
            Serial.print("Flash");Serial.print(i+1);Serial.print(" on, Duration:");
            Serial.println(FlashRunning);
          #endif
          break;

        case 1:
          digitalWrite(Flash2, HIGH);       // switch on FlashLED on 4 o'clock position,
          FlashOn[i] = true;                // set flag and flash duration according to actuel stepspeedand
          FlashRunning = CalcFlashDuration(z);
          #ifdef DEBUG_F
            Serial.print("Flash");Serial.print(i+1);Serial.print(" on, Duration:");
            Serial.println(FlashRunning);
          #endif
          break;

        case 2:                             // no FlashLED on 6 o'clock position
          // digitalWrite(Flash3, HIGH);
          // FlashOn[i] = true;
          // FlashRunning = CalcFlashDuration(z);
          #ifdef DEBUG_F
             Serial.println("nop Flash3");
          #endif
          break;

        case 3:
          digitalWrite(Flash4, HIGH);       // switch on FlashLED on 8 o'clock position,
          FlashOn[i] = true;                // set flag and flash duration according to actuel stepspeedand
          FlashRunning = CalcFlashDuration(z);
          #ifdef DEBUG_F
            Serial.print("Flash");Serial.print(i+1);Serial.print(" on, Duration:");
            Serial.println(FlashRunning);
          #endif      break;
          break;

        case 4:
          digitalWrite(Flash5, HIGH);       // switch on FlashLED on 10 o'clock position,
          FlashOn[i] = true;                // set flag and flash duration according to actuel stepspeedand
          FlashRunning = CalcFlashDuration(z);
          #ifdef DEBUG_F
            Serial.print("Flash");Serial.print(i+1);Serial.print(" on, Duration:");
            Serial.println(FlashRunning);
          #endif
          break;
      }                                     // end switch case
    }                                       // end if, no else needed
  }                                         // end for loop
}

int CalcFlashDuration (int stepS) {         // function to set duration of Flash
                                            // according to turntable rotating speed
  if (stepS > 4) return FlashDurFactor*1;   // Flashduration FlashDurFactor times 20ms
  if (stepS > 3) return FlashDurFactor*2;   // Flashduration FlashDurFactor times 40ms
  if (stepS > 2) return FlashDurFactor*3;   // Flashduration FlashDurFactor times 60ms
  return FlashDurFactor*4;                  // Flashduration FlashDurFactor times 80ms
}                                           // function CalcFlashDuration closed