// This code is 100% AI generated using GitHub Copilot

#include <FastLED.h>

#define NUM_LEDS 50
#define DATA_PIN 3
CRGB leds[NUM_LEDS];

const int trigPin = 4;
const int echoPin = 5;
const float SPEED_OF_SOUND_MM_PER_US = 0.34;
const float INCH_TO_MM = 25.4 * 7; //gwm scale *7 for wokwi range of 400cm
const int NUM_PATTERNS = 6;
const uint8_t WHITE_INSTEAD_OF_HUE = 0;
const int DIST_BRIGHT_MM = 2 * INCH_TO_MM;
const int DIST_DIM_MM = 7 * INCH_TO_MM;
const int HUE_LOW_MM = 9 * INCH_TO_MM;
const int HUE_HIGH_MM = 14 * INCH_TO_MM;
const int HUE_WHITE_MM = 14.5 * INCH_TO_MM;
const int PATTERN_FIRST_MM = 16 * INCH_TO_MM;
const int PATTERN_LAST_MM = 21 * INCH_TO_MM;

struct WINK_STRUCT {
  uint8_t pixelNum;
  uint8_t brightness;
  int8_t direction;
  CRGB color;
};

enum PatternActionEnum { Initialize, Run };
enum LightStateEnum { Off, On };
LightStateEnum LightState = Off;
uint8_t Brightness, Hue, Hue2, Pattern;
CRGB PrimaryColor, SecondaryColor;
bool PatternChanged = false;

void setup() {
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 1000);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600); // gwm added
}

void loop() {
  ReadDistance();
  PrintInfo();
  if (LightState == On) {
    RunPattern(Run);
  }
}

void ReadDistance() {
  static long nextDistance;
  long distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  nextDistance = duration * SPEED_OF_SOUND_MM_PER_US / 2;
  distance = nextDistance; // this is to throw out the last in-range reading before going out-of-range
  if (nextDistance > PATTERN_LAST_MM) {
    distance = nextDistance;
  }
  if (distance < DIST_BRIGHT_MM) {
    LightOff();
  } else if (distance >= DIST_BRIGHT_MM && distance < DIST_DIM_MM) {
    Brightness = map(distance, DIST_BRIGHT_MM, DIST_DIM_MM, 0, 255);
    LightOn();
  } else if (distance >= DIST_DIM_MM && distance < HUE_LOW_MM) {
    LightOff();
  } else if (distance >= HUE_LOW_MM && distance < HUE_HIGH_MM) {
    Hue = map(distance, HUE_LOW_MM, HUE_HIGH_MM, 0, 255);
    LightOn();
  } else if (distance >= HUE_HIGH_MM && distance < HUE_WHITE_MM) {
    Hue = WHITE_INSTEAD_OF_HUE;
    Hue2 = Hue + 128; // It's okay to roll over
    LightOn();
  } else if (distance >= HUE_WHITE_MM && distance < PATTERN_FIRST_MM) {
    LightOff();
  } else if (distance >= PATTERN_FIRST_MM && distance <= PATTERN_LAST_MM) {
    static uint8_t PreviousPattern = 255;
    Pattern = map(distance, PATTERN_FIRST_MM, PATTERN_LAST_MM, 0, NUM_PATTERNS - 1);
    if (Pattern != PreviousPattern) {
      RunPattern(Initialize);
      PreviousPattern = Pattern;
    }
    LightOn();
  }
}

void PrintInfo() {
  EVERY_N_MILLISECONDS(500) {
    Serial.print("LightState: ");
    Serial.print(LightState);
    Serial.print(", Brightness: ");
    Serial.print(Brightness);
    Serial.print(", Hue: ");
    Serial.print(Hue);
    Serial.print(", Pattern: ");
    Serial.println(Pattern);
  }
}

void LightOff() {
  if (LightState == On) {
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    FastLED.show();
    LightState = Off;
  }
}

void LightOn() {
  LightState = On;
  PrimaryColor = CHSV(Hue, (Hue == WHITE_INSTEAD_OF_HUE) ? 0 : 255, Brightness);
  SecondaryColor = CHSV(Hue2, (Hue == WHITE_INSTEAD_OF_HUE) ? 0 : 255, Brightness);
}

void RunPattern(PatternActionEnum patternAction) {
  switch (Pattern) {
    case 0:
      SolidLightPattern(patternAction);
      break;
    case 1:
      RandomDotPattern(patternAction);
      break;
    case 2:
      RandomMulticolorDotPattern(patternAction);
      break;
    case 3:
      WinkOnOffPattern(patternAction);
      break;
    case 4:
      ChaseWormsPattern(patternAction);
      break;
    default:
      SolidLightPattern(patternAction);
      break;
  }
}

void SolidLightPattern(PatternActionEnum patternAction) {
  EVERY_N_MILLISECONDS(50) {
    fill_solid(leds, NUM_LEDS, PrimaryColor);
    FastLED.show();
  }
}

void RandomDotPattern(PatternActionEnum patternAction) {
  EVERY_N_MILLISECONDS(10) {
    int pixel = random(NUM_LEDS);
    leds[pixel] = PrimaryColor;
    FastLED.show();
    leds[pixel] = CRGB::Black;
  }
}

void RandomMulticolorDotPattern(PatternActionEnum patternAction) {
  EVERY_N_MILLISECONDS(10) {
    leds[random(NUM_LEDS)] = CHSV(random(255), random(50, 255), Brightness);
    FastLED.show();
  }
}

void WinkOnOffPattern(PatternActionEnum patternAction) {
  static const int WINK_SIZE = 6;
  static WINK_STRUCT winkArray[WINK_SIZE];
  if (patternAction == Initialize) {
    for (int i = 0; i < WINK_SIZE; ++i) {
      winkArray[i].brightness = random(255);
      winkArray[i].pixelNum = random(NUM_LEDS);
      winkArray[i].direction = +1;
      winkArray[i].color = random(0, 2) ? PrimaryColor : SecondaryColor;
    }
  }
  EVERY_N_MILLISECONDS(80) {
    for (int i = 0; i < WINK_SIZE; ++i) {
      leds[winkArray[i].pixelNum] = winkArray[i].color;
      leds[winkArray[i].pixelNum].fadeToBlackBy(255 - winkArray[i].brightness);
      winkArray[i].brightness += winkArray[i].direction;
      if (winkArray[i].direction == -1 && winkArray[i].brightness == 0) {
        winkArray[i].direction = +1;
        winkArray[i].pixelNum = random(NUM_LEDS);
      }
      if (winkArray[i].brightness == 255) {
        winkArray[i].direction = -1;
      }
    }
    FastLED.show();
  }
}

void ChaseWormsPattern(PatternActionEnum patternAction) {
  static uint8_t worm1[8] = {0};
  static uint8_t worm2[4] = {0};
  static bool movement = false;
  EVERY_N_MILLISECONDS(80) {
    movement = true;
    for (int i = 7; i > 0; --i) {
      worm1[i] = worm1[i - 1];
    }
    worm1[0] = worm1[1] + 1;
    if (worm1[0] >= NUM_LEDS) worm1[0] = 0;
  }
  EVERY_N_MILLISECONDS(45) {
    movement = true;
    for (int i = 3; i > 0; --i) {
      worm2[i] = worm2[i - 1];
    }
    worm2[0] = worm2[1] - 1;
    if (worm2[0] >= NUM_LEDS) worm2[0] = NUM_LEDS - 1; // Using >= to handle uint8_t underflow
  }
  if (movement) {
    for (uint8_t i : worm1) {
      leds[i] = PrimaryColor;
    }
    for (uint8_t i : worm2) {
      leds[i] = SecondaryColor;
    }
    FastLED.show();
    fill_solid(leds, NUM_LEDS, CRGB::Black); // Clear after showing
    movement = false;
  }
}