/*
This program is to control multiple LEDs, to bring some fancy effects to a Imperial Raider

The core idea is:

BlinkLed 1 - 3: run a fiber optic to these leds, and connect these to the sides of the raider, to allow these lights to twinkle/fade
SolidLed1: this is for a cockpit LED, can either be a sold "on" led, or can use a flicker LED to make this animated
LaserLed1: connect fiberoptics from here to each gun, this will have a tripple fire effect on button press
FadeLed: this is for the engines, and will cause the engines to fade on and off

Use 100 (2.0v forward) - 130 (2.2v forward) ohm resistors with this setup to avoid burning out the LEDs

*/

// Pin Definitions
const uint8_t blinkLed1 = 3;
const uint8_t blinkLed2 = 5;
const uint8_t blinkLed3 = 6;
const uint8_t solidLed1 = 8;
const uint8_t laserLed1 = 9;
const uint8_t throbPin1 = 10;
const uint8_t throbPin2 = 11;

// PWM Pins Overview Comment
// Note: Almost all PWM pins are in use (3, 5, 6, 9, 10, 11).
// Three vias are available for engine lights to allow up to 4 engines to be lit.
// Due to the 40mA limit on PWM pins, pins 10 and 11 are set to mirror each other
// to maximize the number of available engine LEDs.

// Brightness Variables
uint8_t brightnessLaser = constrain(brightnessLaser, 0, 255);
uint8_t brightnessBlinkLed1 = constrain(brightnessBlinkLed1, 0, 255);
uint8_t brightnessBlinkLed2 = constrain(brightnessBlinkLed2, 0, 255);
uint8_t brightnessBlinkLed3 = constrain(brightnessBlinkLed3, 0, 255);
uint8_t maxBrightnessLaser = 255;

// Fade Speeds
float laserFade = 0.90;
uint8_t blinkFade = 5;
uint8_t randomWidth = 100; 
uint8_t switchChance = 3;  // Probability of twinkle LEDs switching from fading to brightening (%).

// Button Counter
const uint8_t buttonPin = 2;
uint8_t buttonCounter = 0;
byte buttonState = LOW;
byte lastButtonState = LOW;
unsigned long nextReadMillis = 0;

// LED Variables
byte blinkLed1Value = 1;
byte blinkLed2Value = 1;
byte blinkLed3Value = 1;

// Debounce Time
#define debounceTime 200

// Sine Wave Generator for Engine Throb
float timeStep = 0;
uint8_t throbValue = 0;


void setup() {
    // Debugging Statment
    Serial.begin(115200); // used for debugging, comment out
    
    // LED setup
    pinMode(solidLed1, OUTPUT); // pin "solidLed1" is output
    pinMode(laserLed1, OUTPUT); // pin "laserLed1" is output
    randomSeed(analogRead(0)); // makes the Random() function truly random
    
    // Button Setup
    pinMode(buttonPin, INPUT);  // Initialize the button pin as a input:
   
    // twinkleLeds Setup
    pinMode(blinkLed1, OUTPUT); // Pin "blinkLed1" is output
    pinMode(blinkLed2, OUTPUT); // Pin "blinkLed2" is output
    pinMode(blinkLed3, OUTPUT); // Pin "blinkLed3" is output

    // this is for the sine wave engine throb
    pinMode(10, OUTPUT);
    pinMode(11, OUTPUT);

    // Turns on solid LED, sets cockpit LED to on. Use a flickering LED here
    digitalWrite(solidLed1, HIGH);
 }

void loop() {    
   Serial.println(brightnessBlinkLed1); // for debugging
   
   // Control functions
   turboLaser();
   twinkleLED(brightnessBlinkLed1, blinkLed1, blinkFade, blinkLed1Value); // Controls first twinkle LED
   twinkleLED(brightnessBlinkLed2, blinkLed2, blinkFade, blinkLed2Value); // Controls second twinkle LED
   twinkleLED(brightnessBlinkLed3, blinkLed3, blinkFade, blinkLed3Value); // Controls third twinkle LED
   throbber(timeStep, throbValue); // This is for the sine wave engine throb.
  
   delay(30); // 30ms cycle
  }

void throbber(float &time, uint8_t &throb){ // engine throb function. Produces a sine wave 
    throb = 127 * sin( 2 * PI * time) + 127; // sine equation, sine * Tau + 127 to avoid negitive numbers
    timeStep += 0.01; // used to determine how quickly engines throb, default 0.01 for 3 second pulse.
    
    // Writes throb to engines
    analogWrite(throbPin1, throb); 
    analogWrite(throbPin2, throb); 
  }
  
void twinkleLED(uint8_t &brightness, uint8_t blinkLed, uint8_t fade, byte &value){ //twinkling Side LEDs funtion
  uint8_t blinkLedRandom = 0; // Random Value for LED

  blinkLedRandom = random(randomWidth); // Sets blinkLedRandom to a value between 0 & 199

  if (blinkLedRandom <= switchChance){ // if random number is < switch chance, go negitive
      value = LOW;
    } else if (blinkLedRandom >= randomWidth - switchChance){ // if random number is higher than switch chance, go postiive. if between, maintain direction
      value = HIGH;
  }
  
  brightness = (value == HIGH && brightness < 250) ? brightness + fade : // randomly increases and decreases the brightness
               (value == LOW && brightness > 100) ? brightness - fade :
               (value == LOW && brightness <= 100) ? 0 : brightness;
  analogWrite(blinkLed, brightness);
 }

void turboLaser() {
    // Laser firing logic
    buttonPress();
        if (buttonCounter == 0) { // If button not pressed, do nothing
        brightnessLaser = 0;
    } else if (buttonCounter <= 79) {// if count is <80, and devisible by 20, then laser max. if not devisible by 20, the reduce laser
        brightnessLaser = (buttonCounter % 20 == 0) ? maxBrightnessLaser : brightnessLaser * laserFade;
        buttonCounter++;
    } else { // if buttoncount is 80, then reset and wait for next button press
        brightnessLaser = 0;
        buttonCounter = 0;
    }

    analogWrite(laserLed1, brightnessLaser);
    }

void buttonPress(){
    unsigned long newMillis = millis();

    // Button debounce counter
    if (newMillis >= nextReadMillis) {
        // Compare the buttonState to its previous state
        buttonState = digitalRead(buttonPin);
        if (buttonState != lastButtonState) {
            // If the state has changed, increment the counter
            if (buttonState == HIGH) {
                // If the current state is HIGH, the button went from off to on
                nextReadMillis = newMillis + debounceTime;
                buttonCounter++;
            }
            lastButtonState = buttonState; // Save the current state as the last state
        }
    }
 }