/*
  Forum: https://forum.arduino.cc/t/arduino-sketch-not-working-and-i-dont-know-why/1186468
  Wokwi: https://wokwi.com/projects/380831520756823041
*/

#include<Arduino.h>
#include<Servo.h>


Servo servo;
constexpr int  openAngle   = 45;
constexpr int  closedAngle = 90;
constexpr byte servoPin = 11;

// Analog Output
constexpr byte interiorFloorPin = 6;   // Interior Floor
constexpr byte interiorTopPin   = 9;   // Interior Top
constexpr byte frontBottomPin   = 4;   // Front Bottom

// Digital Output
constexpr byte leftBlinkerPin = 7;    // Left Blinker
constexpr byte rightBlinkerPin = 8;   // Right Blinker
constexpr byte frontTopLeftPin = 3;   // Front Top Left
constexpr byte frontTopRightPin = 10; // Front Top Right 10 instead of 0
constexpr byte rearLeftPin = 5;       // Rear Left
constexpr byte rearRightPin = 2;      // Rear Right 2 instead of 1

constexpr byte interiorButtonPin        = A3; // Interior Button
constexpr byte leftBlinkerButtonPin     = A4; // Left Blinker Button
constexpr byte rightBlinkerButtonPin    = A6; // Right Blinker Button
constexpr byte frontTopButtonPin        = A0; // Front Top Button
constexpr byte frontBottomButtonPin     = A1; // Front Bottom Button
constexpr byte rearButtonPin            = A2; // Rear Button


struct analogLightType {
  byte switchPin;
  byte outPin;
  byte value;
  byte maxValue;
  byte state;
  unsigned long lastChange;
  byte prevState;
  void handle() {
    value = maxValue - value;  // toggles between maxValue and zero
    analogWrite(outPin, value);
  }
  void init(byte switchP, byte outP, byte maxV) {
    switchPin = switchP;
    outPin    = outP;
    maxValue = maxV;
    pinMode(switchPin, INPUT_PULLUP);
    pinMode(outPin, OUTPUT);
    analogWrite(outPin, 0);
    lastChange = 0;
    state = HIGH;
    prevState = HIGH;
  };
  boolean buttonPressed() {
    byte actState = digitalRead(switchPin);
    if (actState != prevState) {
      prevState = actState;
      lastChange = millis();
    }
    if (state != actState && millis() - lastChange > 20) {
      state = actState;
      return !actState;
    }
    return false;
  };
  boolean isOn() {
    return (value == maxValue);
  }
};

struct digitalLightType {
  byte switchPin;
  byte outPin;
  boolean shallBlink;
  byte state = HIGH;
  byte prevState = HIGH;
  unsigned long lastChange = 0;
  void handle() {
    digitalWrite(outPin, state);
    state = !state;
  }
  void init(byte switchP, byte outP) {
    switchPin = switchP;
    outPin    = outP;
    pinMode(switchPin, INPUT_PULLUP);
    pinMode(outPin, OUTPUT);
    off();
    shallBlink = false;
  }
  void doBlink() {
    if (shallBlink)
      if (millis() - lastChange >= 500) {
        lastChange = millis();
        byte actState = digitalRead(outPin);
        digitalWrite(outPin, !actState);
      }
  }
  void toggle() {
    if (shallBlink) {
      off();
    } else {
      shallBlink = true;
    }
  }
  void off() {
    digitalWrite(outPin, LOW);
    shallBlink = false;
  }
  boolean buttonPressed() {
    byte actState = digitalRead(switchPin);
    if (actState != prevState) {
      prevState = actState;
      lastChange = millis();
    }
    if (state != actState && millis() - lastChange > 20) {
      state = actState;
      return !actState;
    }
    return false;
  }
};


analogLightType interiorFloor;
analogLightType interiorTop;
analogLightType frontTopLeft;
analogLightType frontTopRight;

digitalLightType leftBlinker;
digitalLightType rightBlinker;



//*************************************************** void Setup ************************************************
void setup() {
  Serial.begin(115200);
  interiorFloor.init(interiorButtonPin,      interiorFloorPin,  127);
  interiorTop.init(  interiorButtonPin,        interiorTopPin,  127);
  frontTopLeft.init( frontTopButtonPin,       frontTopLeftPin, 127);
  frontTopRight.init(frontTopButtonPin,      frontTopRightPin, 127);

  leftBlinker.init(  leftBlinkerButtonPin,    leftBlinkerPin);
  rightBlinker.init(rightBlinkerButtonPin,   rightBlinkerPin);
  servo.attach(servoPin);
  servo.write(closedAngle);
}

void loop() {
  if (interiorFloor.buttonPressed()) {
    interiorFloor.handle();
    interiorTop.handle();
  }
  if (frontTopLeft.buttonPressed()) {
    frontTopLeft.handle();
    frontTopRight.handle();
    if (frontTopLeft.isOn()) {
      servo.write(openAngle);
    } else {
      servo.write(closedAngle);
    }
  }
  if (leftBlinker.buttonPressed()) {
    leftBlinker.toggle();
    if (leftBlinker.shallBlink) {
      rightBlinker.off();
    }
  }
  if (rightBlinker.buttonPressed()) {
    rightBlinker.toggle();
    if (rightBlinker.shallBlink) {
      leftBlinker.off();
    }
  }
  leftBlinker.doBlink();
  rightBlinker.doBlink();
}