#pragma once

#include "esphome/core/component.h"
#include "esphome/core/gpio.h"
#include "esphome/core/helpers.h"
#include "esphome/components/display/display_buffer.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/time/real_time_clock.h"
#include <cstdarg>
#include <string>

namespace esphome {
namespace nixie_display {

class NixieDisplay : public display::DisplayBuffer,
                     public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST,
                                           spi::CLOCK_POLARITY_HIGH,
                                           spi::CLOCK_PHASE_TRAILING,
                                           spi::DATA_RATE_200KHZ> {
 public:
  enum DisplayMode : uint8_t {
    MODE_UNKNOWN = 0,
    MODE_TIME = 1,
    MODE_DATE = 2,
  };

  void set_anode_pins(InternalGPIOPin *anode0, InternalGPIOPin *anode1,
                      InternalGPIOPin *anode2, InternalGPIOPin *le_pin) {
    this->anode0_pin_ = anode0;
    this->anode1_pin_ = anode1;
    this->anode2_pin_ = anode2;
    this->le_pin_ = le_pin;
  }

  void setup() override;
  void dump_config() override;
  void update() override;
  void loop() override;
  display::DisplayType get_display_type() override {
    return display::DISPLAY_TYPE_BINARY;
  }
  void printf(const char *format, ...);
  void display_text(const char *text) {
    this->last_external_text_ms_ = millis();
    this->set_target_string_(text, MODE_UNKNOWN);
  }
  void display_time_text(const char *text) {
    this->last_external_text_ms_ = millis();
    this->set_target_string_(text, MODE_TIME);
  }
  void display_date_text(const char *text) {
    this->last_external_text_ms_ = millis();
    this->set_target_string_(text, MODE_DATE);
  }
  void set_time_source(time::RealTimeClock *rtc) { this->time_source_ = rtc; }
  void set_debug_logging(bool enabled) { this->debug_logging_ = enabled; }
  void set_anti_poisoning(bool enabled) { this->anti_poisoning_ = enabled; }
  void set_lambda_hold_ms(uint32_t hold_ms) { this->lambda_hold_ms_ = hold_ms; }
  void set_auto_time(bool enabled) { this->auto_time_ = enabled; }
  void set_date_mode(bool enabled) {
    this->date_mode_ = enabled;
    if (enabled) {
      this->auto_rotate_ = false;
    }
  }
  bool is_date_mode() const { return this->date_mode_; }
  void set_auto_rotate(bool enabled) {
    this->auto_rotate_ = enabled;
    if (enabled) {
      this->date_mode_ = false;
    }
  }
  bool is_auto_rotate() const { return this->auto_rotate_; }
  void set_refresh_interval_us(uint32_t interval_us) {
    this->refresh_interval_us_ = interval_us;
  }
  float get_setup_priority() const override { return setup_priority::PROCESSOR; }

 protected:
  void draw_absolute_pixel_internal(int x, int y, Color color) override;
  int get_height_internal() override { return 1; }
  int get_width_internal() override { return NUM_TUBES; }

 private:
  InternalGPIOPin *anode0_pin_{nullptr};
  InternalGPIOPin *anode1_pin_{nullptr};
  InternalGPIOPin *anode2_pin_{nullptr};
  InternalGPIOPin *le_pin_{nullptr};

  // Bitmap for digits 0-9, 10 bits per digit (cathode pins)
  const uint16_t digit_bitmap_[10] = {
      1022,  // 0
      1021,  // 1
      1019,  // 2
      1015,  // 3
      1007,  // 4
      991,   // 5
      959,   // 6
      895,   // 7
      767,   // 8
      511    // 9
  };

  static constexpr int NUM_TUBES = 6;
  static constexpr uint32_t DEFAULT_REFRESH_INTERVAL_US = 1000;
  
  uint32_t last_update_us_;
  uint32_t last_logic_update_ms_{0};
  uint32_t refresh_interval_us_{DEFAULT_REFRESH_INTERVAL_US};
  uint32_t lambda_hold_ms_{1500};
  uint8_t current_anode_;
  std::string displayed_string_;
  std::string target_string_;
  DisplayMode current_mode_{MODE_UNKNOWN};
  DisplayMode target_mode_{MODE_UNKNOWN};
  bool anti_poison_active_;
  unsigned long anti_poison_start_;
  uint8_t anti_poison_counter_;
  HighFrequencyLoopRequester high_freq_;
  time::RealTimeClock *time_source_{nullptr};
  bool debug_logging_{false};
  bool anti_poisoning_{true};
  bool auto_time_{true};
  bool date_mode_{false};
  bool auto_rotate_{false};
  bool anti_poison_on_mode_change_{true};
  bool anti_poison_pending_{false};
  uint32_t last_debug_log_ms_{0};
  uint32_t last_external_text_ms_{0};
  int last_render_second_{-1};
  bool last_effective_date_mode_{false};

  // Anti-poisoning function: cycles through digits to prevent cathode buildup
  std::string anti_poison_transition_(const std::string &from_str,
                                      const std::string &to_str);
  
  // Update the SPI display for current tube pair and anode
  void update_tube_display_(const std::string &display_str);
  
  // Set active anode
  void set_anode_(uint8_t anode_index);
  
  // Convert character to digit (0-9)
  uint8_t char_to_digit_(char c);

  void set_target_string_(const std::string &value, DisplayMode mode);
};

}  // namespace nixie_display
}  // namespace esphome
