#include <Servo.h> // Includes the library to control the Servo motors using Pulse Width Modulation (PWM).

// --- PIN ASSIGNMENTS ---
// These constants define where each component is physically connected to the Arduino Uno.
const int trigPin = 9;      // Ultrasonic sensor Trigger pin: Sends out sound waves.
const int echoPin = 10;     // Ultrasonic sensor Echo pin: Receives reflected sound waves.
const int greenLed = 2;     // Digital Output: System is in a safe state.
const int yellowLed = 3;    // Digital Output: Caution state (Object approaching).
const int redLed = 4;       // Digital Output: Alert state (Object detected).
const int buzzer = 5;       // PWM Output: Provides variable frequency audio feedback.
const int servoPin = 8;     // PWM Output: Actuates the "Bomb Hatch" mechanism.
const int flareServoPin = 6; // PWM Output: Actuates the "Flare Deployment" mechanism.
const int btnFlareMonitor = A0; // Input: Manual button for flares (Uses an Analog pin for Digital logic)[cite: 40].
const int btnBombHatch = A1;    // Input: Manual button for the hatch (Demonstrates pin versatility)[cite: 40].
const int rgbR = 11;        // RGB Channel: Red color control.
const int rgbG = 12;        // RGB Channel: Green color control.
const int rgbB = 13;        // RGB Channel: Blue color control.

// --- ACTUATOR OBJECTS ---
// Creating instances of the Servo class to control our motors independently.
Servo bombHatch;
Servo flareServo;

// --- GLOBAL VARIABLES ---
int distance;                // Stores the calculated distance in centimeters.
int lastValidDistance = 100; // Used to filter out sensor errors/noise.
unsigned long lastBeepTime = 0; // Tracks time for the non-blocking buzzer logic.

void setup() {
  // Configures the data direction for all connected pins[cite: 40].
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(yellowLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  pinMode(buzzer, OUTPUT);
  
  // Uses INPUT_PULLUP to activate internal resistors, removing the need for external ones.
  pinMode(btnFlareMonitor, INPUT_PULLUP); 
  pinMode(btnBombHatch, INPUT_PULLUP);
  
  pinMode(rgbR, OUTPUT);
  pinMode(rgbG, OUTPUT);
  pinMode(rgbB, OUTPUT);
  
  // Assigns the servo objects to their specific control pins.
  bombHatch.attach(servoPin); 
  flareServo.attach(flareServoPin);

  // Sets the initial "Safe" position for the invention (0 degrees).
  bombHatch.write(0);
  flareServo.write(0);
}

void loop() {
  // 1. DISTANCE SENSING LOGIC
  // Generates a 10-microsecond pulse to trigger the ultrasonic sensor.
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // pulseIn measures the time (microseconds) until the sound returns.
  long duration = pulseIn(echoPin, HIGH, 25000); 
  // Physics Calculation: (Time * Speed of Sound) / 2 (for round trip).
  distance = duration * 0.034 / 2; 

  // Basic noise filter: If sensor fails, keep the last known good distance.
  if (distance <= 0) distance = lastValidDistance;
  else lastValidDistance = distance;

  // 2. MECHANICAL CONTROL (HATCH)
  // If button A1 is pressed (LOW state), move the servo to 180 degrees.
  if (digitalRead(btnBombHatch) == LOW) { 
    bombHatch.write(180); 
  } else {
    bombHatch.write(0);   
  }

  // 3. DEFENSE SYSTEM LOGIC (FLARES & RGB)
  // Logic checks if the manual button IS pressed OR if the sensor detects an object < 15cm.
  bool manualFlare = (digitalRead(btnFlareMonitor) == LOW);
  bool autoFlare = (distance < 15 && distance > 2);

  if (manualFlare || autoFlare) {
    flareServo.write(180);  // Trigger flare deployment servo[cite: 40].
    setRGB(255, 0, 0);      // Change RGB LED to RED (Emergency state).
  } else {
    flareServo.write(0);    // Reset servo to standby position.
    setRGB(255, 255, 255);  // Change RGB LED to WHITE (Normal operating state).
  }

  handleAlerts(distance); // Runs the visual and audio feedback system.
}

/** * Function to control the Common Cathode RGB LED.
 * Since it shares a common Ground, sending HIGH (255) turns the color ON.
 */
void setRGB(int r, int g, int b) {
  analogWrite(rgbR, r); 
  analogWrite(rgbG, g); 
  analogWrite(rgbB, b); 
}

/**
 * Manages the LED range indicators and the variable buzzer tones.
 */
void handleAlerts(int d) {
  // Reset all standard LEDs to start with a clean state.
  digitalWrite(greenLed, LOW);
  digitalWrite(yellowLed, LOW);
  digitalWrite(redLed, LOW);
  
  if (d > 40) {
    digitalWrite(greenLed, HIGH); // Safe range.
    noTone(buzzer);               // Disable sound.
  } else if (d <= 40 && d > 15) {
    digitalWrite(yellowLed, HIGH); // Caution range.
    playLockOnTone(d);             // Dynamic beeping.
  } else if (d <= 15 && d > 0) {
    digitalWrite(redLed, HIGH);    // Danger range.
    if (d <= 10) tone(buzzer, 2500); // Continuous high-pitch tone for critical proximity.
    else playLockOnTone(d);
  }
}

/**
 * Creates an "Advanced Geiger Counter" effect using non-blocking timing.
 * This ensures the rest of the project doesn't freeze while beeping[cite: 40, 41].
 */
void playLockOnTone(int d) {
  // Uses map() to turn distance (10cm-40cm) into a delay time (40ms-500ms).
  int pause = map(d, 10, 40, 40, 500); 
  // Uses map() to increase the pitch as the object gets closer.
  int pitch = map(d, 10, 40, 2500, 1200); 
  
  if (millis() - lastBeepTime > pause) {
    tone(buzzer, pitch, 20); // Short 20ms burst of sound.
    lastBeepTime = millis();
  }
}