/*
 * pixelHeader.h
 * 
 * Object-oriented helper library for 7-segment LED digit displays using WS2812B
 * addressable LED strips with FastLED on ESP32.
 * 
 * Hardware Assumptions:
 * - Each 7-segment digit uses 18 LEDs:
 *   - Vertical segments (b, c, e, f): 3 LEDs each
 *   - Horizontal segments (a, d, g): 2 LEDs each
 * 
 * Physical LED Order (per digit):
 *   g -> b -> a -> f -> e -> d -> c
 * 
 * Default Module Layout (74 LEDs total):
 *   digit0 (18) -> digit1 (18) -> dot0 (1) -> dot1 (1) -> digit2 (18) -> digit3 (18)
 * 
 * Segment Naming Convention (standard 7-segment):
 *        a
 *      -----
 *   f |     | b
 *      --g--
 *   e |     | c
 *      -----
 *        d
 * 
 * Author: ESP32 FastLED Helper Library
 * License: MIT
 */

#ifndef PIXEL_HEADER_H
#define PIXEL_HEADER_H

#include <Arduino.h>
#include <FastLED.h>
#include <array>
#include <vector>

// =============================================================================
// CONSTANTS & CONFIGURATION
// =============================================================================

// Segment indices (standard 7-segment naming)
enum Segment : uint8_t {
  SEG_A = 0,
  SEG_B = 1,
  SEG_C = 2,
  SEG_D = 3,
  SEG_E = 4,
  SEG_F = 5,
  SEG_G = 6
};

// Number of LEDs per segment (modifiable if hardware differs)
struct SegmentLedCount {
  static constexpr uint8_t VERTICAL = 3;    // segments b, c, e, f
  static constexpr uint8_t HORIZONTAL = 2;  // segments a, d, g
  
  static constexpr uint8_t counts[7] = {
    HORIZONTAL,  // a
    VERTICAL,    // b
    VERTICAL,    // c
    HORIZONTAL,  // d
    VERTICAL,    // e
    VERTICAL,    // f
    HORIZONTAL   // g
  };
};

// Default physical LED order mapping for one digit: g,b,a,f,e,d,c
// Maps physical order index (0-6) to logical segment (SEG_A..SEG_G)
constexpr std::array<uint8_t, 7> DEFAULT_SEGMENT_MAPPING = {
  SEG_G,  // Physical position 0 -> segment g
  SEG_B,  // Physical position 1 -> segment b
  SEG_A,  // Physical position 2 -> segment a
  SEG_F,  // Physical position 3 -> segment f
  SEG_E,  // Physical position 4 -> segment e
  SEG_D,  // Physical position 5 -> segment d
  SEG_C   // Physical position 6 -> segment c
};

// Segment patterns for digits 0-9 (bit mask: gfedcba)
// Bit set = segment ON
constexpr uint8_t DIGIT_PATTERNS[10] = {
  0b00111111,  // 0: a,b,c,d,e,f
  0b00000110,  // 1: b,c
  0b01011011,  // 2: a,b,d,e,g
  0b01001111,  // 3: a,b,c,d,g
  0b01100110,  // 4: b,c,f,g
  0b01101101,  // 5: a,c,d,f,g
  0b01111101,  // 6: a,c,d,e,f,g
  0b00000111,  // 7: a,b,c
  0b01111111,  // 8: all segments
  0b01101111   // 9: a,b,c,d,f,g
};

// Default colors
namespace Colors {
  constexpr CRGB OFF = CRGB::Black;
  constexpr CRGB DEFAULT_DIGIT = CRGB::White;
  constexpr CRGB DEFAULT_COLON = CRGB::Red;
}

// =============================================================================
// MAIN CLASS: SevenSegmentStrip
// =============================================================================

class SevenSegmentStrip {
public:
  // Constructor
  // Note: Actual FastLED setup must be done by caller before calling begin()
  SevenSegmentStrip();
  
  // Initialize the display
  // @param leds: Pointer to FastLED CRGB array
  // @param totalLeds: Total number of LEDs in the strip
  // @param digitCount: Number of 7-segment digits
  // @param dotCount: Number of colon/separator dots
  // @param ledsPerDigit: LEDs per digit (default 18)
  void begin(CRGB* leds, uint16_t totalLeds, uint8_t digitCount = 4, 
             uint8_t dotCount = 2, uint8_t ledsPerDigit = 18);
  
  // Set the segment mapping (physical LED order)
  // Call this before begin() or call rebuildLayout() after
  void setMapping(const std::array<uint8_t, 7>& newMapping);
  
  // Rebuild internal LED index buffers (call after changing mapping or layout)
  void rebuildLayout();
  
  // Set a specific digit to display a number (0-9)
  // @param digitIndex: Which digit (0-based)
  // @param number: Value 0-9 to display
  // @param color: Color for all segments of this digit
  // @return: true if successful, false if out of bounds
  bool setDigit(uint8_t digitIndex, uint8_t number, CRGB color = Colors::DEFAULT_DIGIT);
  
  // Set a digit using custom segment pattern
  // @param digitIndex: Which digit (0-based)
  // @param segmentMask: Bitmask (gfedcba) indicating which segments are ON
  // @param color: Color for enabled segments
  // @return: true if successful
  bool setDigitRaw(uint8_t digitIndex, uint8_t segmentMask, CRGB color = Colors::DEFAULT_DIGIT);
  
  // Set all four digits to display time HH:MM
  // @param hour: 0-23 (will be shown as-is, caller handles 12/24h conversion)
  // @param minute: 0-59
  // @param digitColor: Color for digits
  void setTime(uint8_t hour, uint8_t minute, CRGB digitColor = Colors::DEFAULT_DIGIT);
  
  // Control colon dots
  void setColon(bool on, CRGB color = Colors::DEFAULT_COLON);
  
  // Enable/disable non-blocking colon blink
  // @param enable: true to blink, false to stop blinking
  // @param periodMs: Blink period in milliseconds (full cycle on+off)
  void setColonBlink(bool enable, uint32_t periodMs = 1000);
  
  // Non-blocking update function - call in loop()
  // Handles colon blinking and future animations
  // @param nowMs: Current time from millis()
  void update(uint32_t nowMs);
  
  // Explicitly call FastLED.show() - separated for caller control
  void show();
  
  // Clear all LEDs to black
  void clear();
  
  // Getters
  uint16_t getTotalLeds() const { return _totalLeds; }
  uint8_t getDigitCount() const { return _digitCount; }
  uint8_t getDotCount() const { return _dotCount; }
  uint8_t getLedsPerDigit() const { return _ledsPerDigit; }
  
  // Advanced: Get LED index range for a specific segment of a digit
  // Useful for custom per-segment animations
  // @param digitIndex: Which digit
  // @param segment: Which segment (SEG_A..SEG_G)
  // @param outStart: Output start LED index
  // @param outCount: Output number of LEDs in this segment
  // @return: true if successful
  bool getSegmentLedRange(uint8_t digitIndex, Segment segment, 
                          uint16_t& outStart, uint8_t& outCount) const;

  // Advanced: Set color for individual segment
  // @param digitIndex: Which digit
  // @param segment: Which segment
  // @param color: Color to set
  // @return: true if successful
  bool setSegmentColor(uint8_t digitIndex, Segment segment, CRGB color);
  
private:
  // Internal structures
  struct SegmentInfo {
    uint16_t startLedIndex;  // First LED index for this segment
    uint8_t ledCount;        // Number of LEDs in this segment
  };
  
  struct DigitInfo {
    SegmentInfo segments[7]; // Info for each segment (SEG_A..SEG_G)
    uint16_t startLedIndex;  // First LED of this digit
  };
  
  struct DotInfo {
    uint16_t ledIndex;       // LED index for this dot
  };
  
  // Member variables
  CRGB* _leds;                    // Pointer to FastLED array
  uint16_t _totalLeds;            // Total LED count
  uint8_t _digitCount;            // Number of digits
  uint8_t _dotCount;              // Number of dots
  uint8_t _ledsPerDigit;          // LEDs per digit
  
  std::array<uint8_t, 7> _mapping; // Current segment mapping
  std::vector<DigitInfo> _digits;  // Digit layout info
  std::vector<DotInfo> _dots;      // Dot layout info
  
  // Colon blink state
  bool _colonBlinkEnabled;
  uint32_t _colonBlinkPeriod;
  uint32_t _colonBlinkLastToggle;
  bool _colonBlinkState;
  CRGB _colonColor;
  
  // Helper methods
  void calculateDigitLayout(uint8_t digitIndex);
  uint16_t calculateDotLedIndex(uint8_t dotIndex) const;
  bool isValidDigitIndex(uint8_t digitIndex) const;
  bool isValidDotIndex(uint8_t dotIndex) const;
};

// =============================================================================
// INLINE UTILITY FUNCTIONS
// =============================================================================

// Helper to convert 2-digit decimal to individual digits
inline void splitDigits(uint8_t value, uint8_t& tens, uint8_t& ones) {
  tens = value / 10;
  ones = value % 10;
}

// Helper to constrain value to 0-9
inline uint8_t constrainDigit(uint8_t value) {
  return value > 9 ? 9 : value;
}

#endif // PIXEL_HEADER_H

