#pragma once
#include <Arduino.h>
#include <Wire.h>

/** @brief Какой чип реально ответил на шине под платой GY-271. */
enum class ChipType : uint8_t {
    Unknown,
    Qmc5883L, // QST Corporation
    Hmc5883L  // Honeywell
};

/** @brief Сырые показания магнитометра по трём осям, приведённые к единому порядку X,Y,Z. */
struct MagSample {
    int16_t x;
    int16_t y;
    int16_t z;
};

/**
 * @brief Драйвер магнитометра для плат "GY-271".
 *
 * Под этим названием на рынке продаются платы с двумя несовместимыми чипами:
 * QMC5883L (QST) и HMC5883L (Honeywell) — разные I2C-адреса и карты регистров.
 * begin() сам определяет, какой чип на шине, и настраивает именно его.
 * Наружу отдаются только сырые оси (без heading/калибровки) — расчёт курса
 * и калибровка Hard-Iron/Soft-Iron зависят от конкретного экземпляра платы
 * и окружения, поэтому реализованы в тестовом стенде (main.cpp), а не здесь.
 */
class Gy271Compass {
public:
    explicit Gy271Compass(TwoWire &wire = Wire);

    /**
     * @brief Инициализирует шину I2C и определяет/настраивает чип на плате.
     * @return false, если ни один из известных адресов (0x0D, 0x1E) не отвечает.
     */
    bool begin(uint8_t sda, uint8_t scl);

    /** @brief Чип, обнаруженный при begin() (ChipType::Unknown, если begin() не удался). */
    ChipType getChipType() const;

    /**
     * @brief Читает сырые показания магнитометра по всем трём осям.
     * @param out Куда записать результат.
     * @return false при ошибке I2C-транзакции (out не изменяется).
     */
    bool readRaw(MagSample &out);

    /** @brief Была ли недавняя успешная транзакция с чипом по шине. */
    bool isAlive(unsigned long timeoutMs = 500) const;

private:
    // --- QMC5883L (QST Corporation, datasheet Rev.1.0/B, "Register Map") ---
    static constexpr uint8_t kQmcAddress     = 0x0D; // I2C device address
    static constexpr uint8_t kQmcRegDataX    = 0x00; // XOUT_L: 6 байт подряд X,Y,Z (LSB,MSB каждая)
    static constexpr uint8_t kQmcRegStatus   = 0x06; // бит0 DRDY, бит1 OVL, бит2 DOR
    static constexpr uint8_t kQmcRegControl1 = 0x09; // MODE, ODR, RNG, OSR
    static constexpr uint8_t kQmcRegControl2 = 0x0A; // бит7 SOFT_RST, бит6 ROL_PNT, бит0 INT_ENB
    static constexpr uint8_t kQmcRegSetReset = 0x0B; // SET/RESET period, рекомендовано записать 0x01
    static constexpr uint8_t kQmcRegChipId   = 0x0D; // фиксированное значение 0xFF
    static constexpr uint8_t kQmcChipIdValue = 0xFF;
    static constexpr uint8_t kQmcControl1Value = 0x1D; // MODE=Continuous, ODR=200Hz, RNG=±8G, OSR=512

    // --- HMC5883L (Honeywell, "3-Axis Digital Compass IC HMC5883L") ---
    static constexpr uint8_t kHmcAddress      = 0x1E; // I2C device address
    static constexpr uint8_t kHmcRegConfigA   = 0x00; // Configuration Register A
    static constexpr uint8_t kHmcRegConfigB   = 0x01; // Configuration Register B (gain)
    static constexpr uint8_t kHmcRegMode      = 0x02; // Mode Register
    static constexpr uint8_t kHmcRegDataXMsb  = 0x03; // данные идут блоком 0x03-0x08,
                                                        // но физический порядок в регистрах —
                                                        // X(0x03-0x04), Z(0x05-0x06), Y(0x07-0x08),
                                                        // не X,Y,Z! См. THEORY.md, раздел "Регистры".
    static constexpr uint8_t kHmcRegIdA       = 0x0A; // Identification Register A ("H")
    static constexpr uint8_t kHmcIdAValue     = 0x48; // ASCII 'H'
    static constexpr uint8_t kHmcConfigAValue = 0x70; // 8-sample avg, 15Hz, normal measurement
    static constexpr uint8_t kHmcConfigBValue = 0x20; // gain по умолчанию (±1.3 Ga)
    static constexpr uint8_t kHmcModeContinuous = 0x00;

    TwoWire *_wire;
    ChipType _chip = ChipType::Unknown;
    unsigned long _lastOkMs = 0;

    bool beginQmc5883();
    bool beginHmc5883();
    bool readRawQmc5883(MagSample &out);
    bool readRawHmc5883(MagSample &out);

    bool writeRegister(uint8_t address, uint8_t reg, uint8_t value);
    bool readRegisters(uint8_t address, uint8_t reg, uint8_t *buf, uint8_t len);
};
