// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
 * @file fdc2214.cpp
 * @brief FDC2114/2214 capacitance-to-digital codec + ESP32 binding (see fdc2214.h).
 */

#include "services/fdc2214/fdc2214.h"
#include "ServerConfig.h"

#if DETWS_ENABLE_FDC2214

uint32_t fdc2214_data(uint16_t msb_reg, uint16_t lsb_reg)
{
    return ((uint32_t)(msb_reg & 0x0FFF) << 16) | lsb_reg;
}

uint8_t fdc2214_error(uint16_t msb_reg)
{
    return (uint8_t)((msb_reg >> 12) & 0x0F);
}

uint64_t fdc2214_sensor_freq_hz(uint32_t data28, uint32_t fref_hz)
{
    return ((uint64_t)data28 * fref_hz) >> 28;
}

size_t fdc2214_build_config(uint8_t *buf, size_t cap, uint16_t rcount, uint16_t settlecount)
{
    if (!buf || cap < FDC2214_CONFIG_MAX)
        return 0;
    // (register, value) writes; CONFIG is written last because it starts the conversion.
    const uint16_t seq[][2] = {
        {FDC2214_REG_RCOUNT_CH0, rcount},
        {FDC2214_REG_SETTLECOUNT_CH0, settlecount},
        {FDC2214_REG_CLOCK_DIVIDERS_CH0, 0x1001}, // FIN_SEL=1, FREF_DIVIDER=1
        {FDC2214_REG_DRIVE_CURRENT_CH0, 0x8C40},  // mid drive current
        {FDC2214_REG_ERROR_CONFIG, 0x0000},       // no error reporting on INTB
        {FDC2214_REG_MUX_CONFIG, 0x020D},         // single channel CH0, 10 MHz deglitch
        {FDC2214_REG_CONFIG, 0x1E01},             // active CH0, internal ref, full current, start
    };
    size_t o = 0;
    for (size_t i = 0; i < sizeof(seq) / sizeof(seq[0]); i++)
    {
        buf[o++] = (uint8_t)seq[i][0];
        buf[o++] = (uint8_t)(seq[i][1] >> 8);
        buf[o++] = (uint8_t)seq[i][1];
    }
    return o;
}

#if defined(ARDUINO)

#include "services/i2c.h"
#include <Arduino.h>
#include <Wire.h>

namespace
{
// All FDC2214 I2C-binding state, owned by one instance (internal linkage): the device address,
// so it is one named owner, unreachable from any other translation unit.
struct Fdc2214Ctx
{
    uint8_t addr = 0x2A;
};
Fdc2214Ctx s_fdc;

bool read16(uint8_t reg, uint16_t *out)
{
    Wire.beginTransmission(s_fdc.addr);
    Wire.write(reg);
    if (Wire.endTransmission(false) != 0)
        return false;
    if (Wire.requestFrom((int)s_fdc.addr, 2) != 2)
        return false;
    uint16_t hi = Wire.read();
    uint16_t lo = Wire.read();
    *out = (uint16_t)((hi << 8) | lo);
    return true;
}

bool write16(uint8_t reg, uint16_t val)
{
    Wire.beginTransmission(s_fdc.addr);
    Wire.write(reg);
    Wire.write((uint8_t)(val >> 8));
    Wire.write((uint8_t)val);
    return Wire.endTransmission() == 0;
}
} // namespace

bool fdc2214_begin(uint8_t addr, uint16_t rcount, uint16_t settlecount)
{
    detws_i2c_begin();
    s_fdc.addr = addr;
    uint16_t id = 0;
    if (!read16(FDC2214_REG_DEVICE_ID, &id))
        return false;
    if (id != FDC2214_DEVICE_ID && id != 0x3054) // 0x3054 = FDC2114 (12-bit sibling)
        return false;
    uint8_t seq[FDC2214_CONFIG_MAX];
    size_t n = fdc2214_build_config(seq, sizeof(seq), rcount, settlecount);
    for (size_t i = 0; i + 3 <= n; i += 3)
        if (!write16(seq[i], (uint16_t)((seq[i + 1] << 8) | seq[i + 2])))
            return false;
    return true;
}

bool fdc2214_read_ch0(uint32_t *out)
{
    if (!out)
        return false;
    uint16_t msb = 0, lsb = 0;
    if (!read16(FDC2214_REG_DATA_CH0_MSB, &msb) || !read16(FDC2214_REG_DATA_CH0_LSB, &lsb))
        return false;
    *out = fdc2214_data(msb, lsb);
    return true;
}

#endif // ARDUINO

#endif // DETWS_ENABLE_FDC2214
