#include "LogicKiste_StarterShields.h"
#include <IRremote.h>
#include "Shield_MultiFunction.h"

// ==========================================
// 1. SHIELD: MULTI FUNCTION
// ==========================================


// Segment Maps (Active Low)
const uint8_t SEGMENT_SELECT[] = {0xF1, 0xF2, 0xF4, 0xF8};
const uint8_t SEGMENT_MAP_DIGIT[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};

Shield_MultiFunction::Shield_MultiFunction() : 
  btn1(A1), btn2(A2), btn3(A3), _lastDisplayUpdate(0), _currentDigit(0),
  _buzzerTurnOffTime(0), _buzzerToneActive(false) {
  clearDisplay();
}

void Shield_MultiFunction::begin() {
  // LEDs (13, 12, 11, 10)
  for(int i=10; i<=13; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH); // HIGH = AUS bei diesem Shield
  }
  
  // Buzzer (3)
  pinMode(_buzzerPin, OUTPUT);
  digitalWrite(_buzzerPin, HIGH); // HIGH = AUS

  // Display Shift Register
  pinMode(_latchPin, OUTPUT);
  pinMode(_clkPin, OUTPUT);
  pinMode(_dataPin, OUTPUT);

  // Buttons initialisieren
  btn1.begin();
  btn2.begin();
  btn3.begin();
}

void Shield_MultiFunction::update() {
  // 1. Taster aktualisieren
  btn1.update();
  btn2.update();
  btn3.update();

  // 2. Display Multiplexing (alle 4000 Mikrosekunden = 4ms pro Ziffer -> flimmerfrei)
  if (micros() - _lastDisplayUpdate > 4000) {
    _lastDisplayUpdate = micros();
    
    _writeDigit(_currentDigit, _displayMemory[_currentDigit]);
    
    _currentDigit++;
    if (_currentDigit > 3) _currentDigit = 0;
  }

  // 3. Buzzer abschalten, wenn die Zeit abgelaufen ist
  if (_buzzerToneActive && millis() >= _buzzerTurnOffTime) {
    noTone(_buzzerPin);
    digitalWrite(_buzzerPin, HIGH); // Zwingt den Pin auf AUS (Active-Low)
    _buzzerToneActive = false;
  }
}

// --- LEDs ---
void Shield_MultiFunction::setLED(uint8_t ledNum, bool state) {
  uint8_t pin = 14 - ledNum; // D1=13, D2=12, D3=11, D4=10
  if(pin >= 10 && pin <= 13) {
    digitalWrite(pin, !state); // Invertiert, da Low-Aktiv
  }
}

void Shield_MultiFunction::setLEDPWM(uint8_t ledNum, uint8_t value) {
  uint8_t pin = 14 - ledNum;
  if(pin == 11 || pin == 10) { // Nur D3 und D4 können PWM
    analogWrite(pin, 255 - value); // Invertiertes PWM
  }
}

// --- Buzzer ---
void Shield_MultiFunction::setBuzzer(bool state) {
  noTone(_buzzerPin); // Falls ein Ton lief, abwürgen
  _buzzerToneActive = false;
  digitalWrite(_buzzerPin, !state);
}

void Shield_MultiFunction::playTone(uint16_t freq, uint32_t ms) {
  tone(_buzzerPin, freq); // Kein ms-Parameter für die Arduino-Funktion
  _buzzerTurnOffTime = millis() + ms;
  _buzzerToneActive = true;
}

// --- Poti ---
uint16_t Shield_MultiFunction::readPoti() {
  return analogRead(A0);
}

// --- Display ---
void Shield_MultiFunction::showNumber(int num) {
  if (num > 9999 || num < -999) {
    showWord('E', 'R', 'R', ' '); // Overflow Error
    return;
  }
  
  clearDisplay(); // Array mit 0xFF (Leer) füllen
  
  bool isNegative = (num < 0);
  if (isNegative) num = -num;
  
  if (num == 0) {
    _displayMemory[3] = SEGMENT_MAP_DIGIT[0];
    return;
  }

  uint8_t idx = 3;
  while (num > 0 && idx < 4) { // idx ist unsigned, also Trick mit Abbruchbedingung < 4 nutzen (overflow nach 0)
    _displayMemory[idx] = SEGMENT_MAP_DIGIT[num % 10];
    num /= 10;
    idx--;
  }
  
  if (isNegative && idx < 4) {
    _displayMemory[idx] = 191; // Minus-Zeichen
  }
}

void Shield_MultiFunction::showWord(char c1, char c2, char c3, char c4) {
  _displayMemory[0] = _charToSegment(c1);
  _displayMemory[1] = _charToSegment(c2);
  _displayMemory[2] = _charToSegment(c3);
  _displayMemory[3] = _charToSegment(c4);
}

void Shield_MultiFunction::clearDisplay() {
  for(int i=0; i<4; i++) {
    _displayMemory[i] = 255; // 255 = Alle Segmente aus
  }
}

void Shield_MultiFunction::_writeDigit(uint8_t digit, uint8_t value) {
  digitalWrite(_latchPin, LOW);
  shiftOut(_dataPin, _clkPin, MSBFIRST, value); 
  shiftOut(_dataPin, _clkPin, MSBFIRST, SEGMENT_SELECT[digit]);
  digitalWrite(_latchPin, HIGH);
}

uint8_t Shield_MultiFunction::_charToSegment(char c) {
  // Stark vereinfachter ASCII zu 7-Segment Konverter für unsere Dropdowns
  switch (c) {
    case 'A': case 'a': return 136;
    case 'B': case 'b': return 131;
    case 'C':           return 167;
    case 'c':           return 167; 
    case 'D': case 'd': return 161;
    case 'E': case 'e': return 134;
    case 'F': case 'f': return 142;
    case 'H': case 'h': return 137;
    case 'I': case 'i': return 249;
    case 'L': case 'l': return 199;
    case 'O': case 'o': return 163;
    case 'P': case 'p': return 140;
    case 'S': case 's': return 146;
    case 'U': case 'u': return 193;
    case '-':           return 191;
    default:            return 255; // Leerzeichen oder Unbekannt
  }
}


// ==========================================
// 2. SHIELD: RICH SHIELD 1
// ==========================================
#include "Shield_RichShield1.h"

Shield_RichShield1::Shield_RichShield1() : 
  btnK1(9), 
  btnK2(8),
  _display(10, 11),
  _currentIRCode(0),
  _newIRCodeAvailable(false)
{
}

void Shield_RichShield1::begin() {
  const int ledPins[] = {_pinLed1_Yellow, _pinLed2_Blue, _pinLed3_Green, _pinLed4_Red};
  for(int i = 0; i < 4; i++) {
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW);
  }
  
  pinMode(_pinBuzzer, OUTPUT);
  digitalWrite(_pinBuzzer, LOW);

  btnK1.begin();
  btnK2.begin();

  _display.setBrightness(0x0f); 
  delay(10);
  clearDisplay();

  IrReceiver.begin(_pinIR, ENABLE_LED_FEEDBACK); // IR starten
  
  delay(50);
}

void Shield_RichShield1::update() {
  btnK1.update();
  btnK2.update();
  
  _newIRCodeAvailable = false;
  if (IrReceiver.decode()) {
    _currentIRCode = IrReceiver.decodedIRData.decodedRawData;
    _newIRCodeAvailable = true;
    IrReceiver.resume();
  }
}

// --- IR Empfänger ---
void Shield_RichShield1::printIRCode() {
  if (_newIRCodeAvailable) {
    Serial.print("IR Code: 0x");
    Serial.println(_currentIRCode, HEX);
  }
}

bool Shield_RichShield1::checkIRCode(uint32_t hexCode) {
  return (_newIRCodeAvailable && _currentIRCode == hexCode);
}

// --- LEDs ---
void Shield_RichShield1::setLED(uint8_t ledNum, bool state) {
  switch(ledNum) {
    case 1: digitalWrite(_pinLed1_Yellow, state); break;
    case 2: digitalWrite(_pinLed2_Blue, state); break;
    case 3: digitalWrite(_pinLed3_Green, state); break;
    case 4: digitalWrite(_pinLed4_Red, state); break;
  }
}

void Shield_RichShield1::setLEDPWM(uint8_t ledNum, uint8_t value) {
  int pin = -1;
  switch(ledNum) {
    case 1: pin = _pinLed1_Yellow; break;
    case 2: pin = _pinLed2_Blue; break; 
    case 3: pin = _pinLed3_Green; break;   
    case 4: pin = _pinLed4_Red; break;
  }
  
  if (pin == _pinLed2_Blue || pin == _pinLed3_Green) {
    analogWrite(pin, value);
  } else {
    digitalWrite(pin, value > 127 ? HIGH : LOW);
  }
}

// --- Buzzer ---
void Shield_RichShield1::playTone(uint16_t freq, uint32_t ms) {
  tone(_pinBuzzer, freq, ms);
}

// --- Sensoren ---
uint16_t Shield_RichShield1::readPoti() {
  return analogRead(_pinPoti);
}

uint8_t Shield_RichShield1::readLDR() {
  int raw = analogRead(_pinLDR);
  return map(raw, 0, 1023, 0, 100);
}

float Shield_RichShield1::readNTC() {
  int raw = analogRead(_pinNTC);
  if (raw == 0 || raw == 1023) return 0.0;
  
  float r = 10000.0 * (1023.0 / (float)raw - 1.0);
  float tempC = 1.0 / (log(r / 10000.0) / 3950.0 + 1.0 / 298.15) - 273.15;
  
  return tempC;
}

float Shield_RichShield1::readVoltage() {
  int raw = analogRead(_pinVoltage);
  return (raw * 5.0 * 5.0) / 1023.0; 
}

float Shield_RichShield1::readTemperature() {
  _dht.read(_pinDHT);
  return _dht.temperature;
}

float Shield_RichShield1::readHumidity() {
  _dht.read(_pinDHT);
  return _dht.humidity;
}

// --- Display ---
void Shield_RichShield1::showNumber(int num) {
  _display.showNumberDec(num);
}

void Shield_RichShield1::showWord(char c1, char c2, char c3, char c4) {
  uint8_t data[] = {
    _charToSegment(c1),
    _charToSegment(c2),
    _charToSegment(c3),
    _charToSegment(c4)
  };
  _display.setSegments(data);
}

void Shield_RichShield1::clearDisplay() {
  uint8_t data[] = {0, 0, 0, 0};
  _display.setSegments(data);
}

uint8_t Shield_RichShield1::_charToSegment(char c) {
  switch (c) {
    case 'H': case 'h': return 0b01110110; 
    case 'A': case 'a': return 0b01110111; 
    case 'b':           return 0b01111100; 
    case 'C':           return 0b00111001; 
    case 'd':           return 0b01011110; 
    case 'E': case 'e': return 0b01111001; 
    case 'F': case 'f': return 0b01110001; 
    case 'I': case 'i': return 0b00000110; 
    case 'L': case 'l': return 0b00111000; 
    case 'O':           return 0b00111111; 
    case 'o':           return 0b01011100;
    case 'P': case 'p': return 0b01110011; 
    case 'S': case 's': return 0b01101101; 
    case 'U':           return 0b00111110;
    case 'u':           return 0b00011100; 
    case '-':           return 0b01000000; 
    case ' ':           return 0b00000000; 
    default:            return 0b00000000;
  }
}


// ==========================================
// 3. SHIELD: RICH SHIELD 2
// ==========================================
#include "Shield_RichShield2.h"

// FastLED verlangt den Pin zwingend als Präprozessor-Makro für das Template
#define PIN_RGB A3 

Shield_RichShield2::Shield_RichShield2() : 
  btnEncoder(4), // SW Pin
  _oneWire(5),   // DS18B20 Pin
  _sensors(&_oneWire),
  _batteryDisplay(10, 11), // CLK, DIO
  _encoderPos(0),
  _lastEncCLK(HIGH)
{
}

void Shield_RichShield2::begin() {
  // Encoder Pins
  pinMode(_pinEncCLK, INPUT);
  pinMode(_pinEncDT, INPUT);
  btnEncoder.begin();

  // Ultraschall & PIR
  pinMode(_pinUsTrig, OUTPUT);
  pinMode(_pinUsEcho, INPUT);
  pinMode(_pinPIR, INPUT);

  // DS18B20
  _sensors.begin();

  // Batterie Anzeige
  _batteryDisplay.init();
  _batteryDisplay.set(2); 
  _batteryDisplay.displayLevel(0);

  // 16x8 Matrix
  _matrix.begin(0x70);
  _matrix.setRotation(1);
  _matrix.setTextWrap(false);
  _matrix.setTextColor(LED_ON);
  _matrix.clear();
  _matrix.writeDisplay();

  // RGB LEDs (WS2812)
  FastLED.addLeds<WS2812, PIN_RGB, GRB>(_leds, 4);
  FastLED.setBrightness(50);
  clearRGB();
  
  // Initiale Encoder-Messung
  _lastEncCLK = digitalRead(_pinEncCLK);
}

void Shield_RichShield2::update() {
  btnEncoder.update();

  // Sehr schlankes Polling für den Encoder
  int currentCLK = digitalRead(_pinEncCLK);
  if (currentCLK != _lastEncCLK && currentCLK == HIGH) {
    if (digitalRead(_pinEncDT) != currentCLK) {
      _encoderPos++;
    } else {
      _encoderPos--;
    }
  }
  _lastEncCLK = currentCLK;
}

// --- Encoder ---
int Shield_RichShield2::getEncoderPosition() { return _encoderPos; }
void Shield_RichShield2::resetEncoder() { _encoderPos = 0; }

// --- Sensoren ---
float Shield_RichShield2::getTemperature() {
  _sensors.requestTemperatures(); 
  return _sensors.getTempCByIndex(0);
}

long Shield_RichShield2::getDistance() {
  digitalWrite(_pinUsTrig, LOW);
  delayMicroseconds(2);
  digitalWrite(_pinUsTrig, HIGH);
  delayMicroseconds(10);
  digitalWrite(_pinUsTrig, LOW);
  long duration = pulseIn(_pinUsEcho, HIGH, 30000); 
  if (duration == 0) return 999; // Nichts gefunden / Out of Range
  return (duration / 2) / 29.1;
}

bool Shield_RichShield2::isMotionDetected() {
  return digitalRead(_pinPIR) == HIGH;
}

int Shield_RichShield2::getMicLevel() {
  return analogRead(_pinMic);
}

int Shield_RichShield2::getMicPercent() {
  int raw = analogRead(_pinMic);
  int percent = map(raw, 0, 1023, 0, 100);
  return constrain(percent, 0, 100);
}

// --- Joystick ---
int Shield_RichShield2::getJoyX() { return analogRead(_pinJoyX); }
int Shield_RichShield2::getJoyY() { return analogRead(_pinJoyY); }

String Shield_RichShield2::getJoyDirection() {
  int x = analogRead(_pinJoyX);
  int y = analogRead(_pinJoyY);
  if (x < 200) return "LINKS";
  if (x > 800) return "RECHTS";
  if (y < 200) return "UNTEN";
  if (y > 800) return "OBEN";
  return "MITTE";
}

// --- Ausgaben ---
void Shield_RichShield2::setBatteryLevel(uint8_t level) {
  if(level > 7) level = 7;
  _batteryDisplay.displayLevel(level);
}

void Shield_RichShield2::setBatteryPercent(uint8_t percent) {
  uint8_t level = map(percent, 0, 100, 0, 7);
  _batteryDisplay.displayLevel(level);
}

void Shield_RichShield2::setRGB(uint8_t index, uint8_t r, uint8_t g, uint8_t b) {
  if(index < 4) {
    _leds[index] = CRGB(r, g, b);
    FastLED.show();
  }
}

void Shield_RichShield2::clearRGB() {
  FastLED.clear();
  FastLED.show();
}

// --- Matrix ---
void Shield_RichShield2::showMatrixText(String text) {
  _matrix.clear();
  _matrix.setCursor(0, 0);
  _matrix.print(text);
  _matrix.writeDisplay();
}

// --- NEUER BLOCK FÜR DIE LAUFSCHRIFT ---
void Shield_RichShield2::showMatrixTextScroll(String text, int speed) {
  // Ein Standard-Zeichen der Adafruit-GFX ist 5 Pixel breit + 1 Pixel Abstand = 6 Pixel
  int textWidth = text.length() * 6; 
  
  // Wir starten ganz rechts (außerhalb des 16-Pixel-Displays) und schieben nach links
  for (int x = 16; x >= -textWidth; x--) {
    _matrix.clear();
    _matrix.setCursor(x, 0);
    _matrix.print(text);
    _matrix.writeDisplay();
    delay(speed); // Tempo der Laufschrift
  }
}

void Shield_RichShield2::showMatrixNumber(int num) {
  _matrix.clear();
  _matrix.setCursor(2, 1);
  _matrix.print(num);
  _matrix.writeDisplay();
}

void Shield_RichShield2::setMatrixPixel(int x, int y, bool state) {
  _matrix.drawPixel(x, y, state ? LED_ON : LED_OFF);
  _matrix.writeDisplay();
}

void Shield_RichShield2::clearMatrix() {
  _matrix.clear();
  _matrix.writeDisplay();
}


// ==========================================
// 4. SHIELD: KEYESTUDIO
// ==========================================
#include "Shield_Keyestudio.h"

Shield_Keyestudio::Shield_Keyestudio() : 
  btn1(_pinBtn1), 
  btn2(_pinBtn2),
  _currentIRCode(0),
  _newIRCodeAvailable(false),
  _lastDHTRead(0),
  _lastDHTTemp(0.0),
  _lastDHTHum(0.0)
{
}

void Shield_Keyestudio::begin() {
  pinMode(_pinLedRed, OUTPUT);
  pinMode(_pinLedBlue, OUTPUT);
  pinMode(_pinLedR, OUTPUT);
  pinMode(_pinLedG, OUTPUT);
  pinMode(_pinLedB, OUTPUT);
  pinMode(_pinBuzzer, OUTPUT);

  digitalWrite(_pinLedRed, LOW);
  digitalWrite(_pinLedBlue, LOW);
  digitalWrite(_pinBuzzer, HIGH);

  btn1.begin();
  btn2.begin();

  IrReceiver.begin(_pinIR, ENABLE_LED_FEEDBACK);
  
  _updateDHT();
}

void Shield_Keyestudio::update() {
  btn1.update();
  btn2.update();
  
  _newIRCodeAvailable = false;
  if (IrReceiver.decode()) {
    _currentIRCode = IrReceiver.decodedIRData.decodedRawData;
    _newIRCodeAvailable = true;
    IrReceiver.resume();
  }
}

// --- IR Empfänger ---
void Shield_Keyestudio::printIRCode() {
  if (_newIRCodeAvailable) {
    Serial.print("IR Code: 0x");
    Serial.println(_currentIRCode, HEX);
  }
}

bool Shield_Keyestudio::checkIRCode(uint32_t hexCode) {
  return (_newIRCodeAvailable && _currentIRCode == hexCode);
}

// --- LEDs ---
void Shield_Keyestudio::setSingleLED(uint8_t ledNum, bool state) {
  if (ledNum == 1) digitalWrite(_pinLedRed, state);
  if (ledNum == 2) digitalWrite(_pinLedBlue, state);
}

void Shield_Keyestudio::setRGB(uint8_t r, uint8_t g, uint8_t b) {
  analogWrite(_pinLedR, r);
  analogWrite(_pinLedG, g);
  analogWrite(_pinLedB, b);
}

// --- Buzzer ---
void Shield_Keyestudio::playTone(uint16_t freq, uint32_t ms) {
  tone(_pinBuzzer, freq, ms);
}

// --- Sensoren ---
uint16_t Shield_Keyestudio::readPoti() {
  return analogRead(_pinPoti);
}

uint8_t Shield_Keyestudio::readLDR() {
  int raw = analogRead(_pinLDR);
  return map(raw, 0, 1023, 0, 100);
}

float Shield_Keyestudio::readLM35() {
  int raw = analogRead(_pinLM35);
  return (raw * 500.0) / 1023.0;
}

// DHT Helper
void Shield_Keyestudio::_updateDHT() {
  if (millis() - _lastDHTRead > 2000 || _lastDHTRead == 0) {
    int chk = _dht.read(_pinDHT);
    if (chk == DHTLIB_OK) {
      _lastDHTTemp = _dht.temperature;
      _lastDHTHum = _dht.humidity;
    }
    _lastDHTRead = millis();
  }
}

float Shield_Keyestudio::readDHTTemp() {
  _updateDHT();
  return _lastDHTTemp;
}

float Shield_Keyestudio::readDHTHum() {
  _updateDHT();
  return _lastDHTHum;
}