#include "bsp_i2c.h"

#include "bsp_uart.h"
#include "log.h"

#undef LOG_ENABLE
#define LOG_ENABLE 1

#undef LOG_TAG
#define LOG_TAG "I2C"

// #define CONFIG_I2C_DEBUG

#ifdef CONFIG_I2C_DEBUG
#define I2C_DBG(...) logDebug(__VA_ARGS__)
#else
#define I2C_DBG(...)
#endif

static volatile uint8_t i2c_state;
static volatile uint8_t i2c_slave_addr_rw;
static volatile uint8_t i2c_send_stop;   // should the transaction end with a stop
static volatile uint8_t i2c_in_repstart; // in the middle of a repeated start

static uint8_t i2c_master_buffer[I2C_BUFFER_LENGTH];
static volatile uint8_t i2c_master_buffer_index;
static uint8_t i2c_master_buffer_length;

static uint8_t i2c_slave_txbuffer[I2C_BUFFER_LENGTH];
static volatile uint8_t i2c_slave_txbuffer_index;
static uint8_t i2c_slave_txbuffer_length;

static uint8_t i2c_slave_rxbuffer[I2C_BUFFER_LENGTH];
static volatile uint8_t i2c_slave_rxbuffer_index;

static uint8_t is_nack_sent = false;

static volatile uint8_t i2c_error;

static struct i2c_slave_cb *slave_cb = NULL;

#define MASTER_ADDR 0x42

uint8_t i2c_tx_data[2];
// __attribute__((aligned(4)))
uint8_t i2c_rx_data[8];

void BSP_I2C_Init(uint8_t address)
{
    i2c_state       = I2C_READY;
    i2c_send_stop   = true;
    i2c_in_repstart = false;

    GPIOB_ModeCfg(GPIO_Pin_14 | GPIO_Pin_15, GPIO_ModeIN_PU);

    I2C_Init(I2C_Mode_I2C, 400000, I2C_DutyCycle_16_9, I2C_Ack_Enable, I2C_AckAddr_7bit, address);

    I2C_ITConfig(I2C_IT_BUF, ENABLE);
    I2C_ITConfig(I2C_IT_EVT, ENABLE);
    I2C_ITConfig(I2C_IT_ERR, ENABLE);

    PFIC_EnableIRQ(I2C_IRQn);

    logDebug("BSP_I2C_Init(%02x)", address);
}

void i2c_slave_cb_register(struct i2c_slave_cb *cb)
{
    slave_cb = cb;
}

int I2C_Write(uint8_t addr_7bit, const uint8_t *data, uint8_t length,
              uint8_t wait, uint8_t send_stop)
{
    if (length > I2C_BUFFER_LENGTH)
    {
        return -I2C_NO_MEM;
    }

    if (i2c_state != I2C_READY)
    {
        return -I2C_STATE;
    }

    if (!length)
    {
        return 0;
    }

    i2c_state     = I2C_MTX;
    i2c_send_stop = send_stop;

    i2c_error = 0;

    // initialize buffer iteration vars
    i2c_master_buffer_index  = 0;
    i2c_master_buffer_length = length;

    tmos_memcpy(i2c_master_buffer, data, length);

    i2c_slave_addr_rw = I2C_WRITE;
    i2c_slave_addr_rw |= addr_7bit << 1;

    I2C_GenerateSTOP(DISABLE);

    if (i2c_in_repstart == true)
    {
        i2c_in_repstart = false;

        do {
            I2C_SendData(i2c_slave_addr_rw);
        } while (R16_I2C_CTRL1 & RB_I2C_BTF);

        /* Disabled in IRS */
        I2C_ITConfig(I2C_IT_BUF, ENABLE);
        I2C_ITConfig(I2C_IT_EVT, ENABLE);
        I2C_ITConfig(I2C_IT_ERR, ENABLE);
    }
    else
    {
        I2C_GenerateSTART(ENABLE);
    }

    while (wait && (i2c_state == I2C_MTX))
    {
        continue;
    }

    if (i2c_error)
    {
        return -i2c_error;
    }

    return 0;
}

int I2C_Read(uint8_t addr_7bit, uint8_t *data, uint8_t length,
             uint8_t send_stop, int timeout)
{
    int to          = 0;
    uint8_t forever = (timeout == -1);

    if (length > I2C_BUFFER_LENGTH)
    {
        return -I2C_NO_MEM;
    }

    if (i2c_state != I2C_READY)
    {
        return -I2C_STATE;
    }

    if (!length)
    {
        return 0;
    }

    i2c_state     = I2C_MRX;
    i2c_send_stop = send_stop;

    i2c_error = 0;

    // initialize buffer iteration vars
    i2c_master_buffer_index  = 0;
    i2c_master_buffer_length = length - 1;

    i2c_slave_addr_rw = I2C_READ;
    i2c_slave_addr_rw |= addr_7bit << 1;

    I2C_GenerateSTOP(DISABLE);

    if (i2c_in_repstart == true)
    {
        i2c_in_repstart = false;

        do {
            I2C_SendData(i2c_slave_addr_rw);
        } while (R16_I2C_CTRL1 & RB_I2C_BTF);

        /* Disabled in IRS */
        I2C_ITConfig(I2C_IT_BUF, ENABLE);
        I2C_ITConfig(I2C_IT_EVT, ENABLE);
        I2C_ITConfig(I2C_IT_ERR, ENABLE);
    }
    else
    {
        I2C_GenerateSTART(ENABLE);
    }

    // wait for read operation to complete
    while (i2c_state == I2C_MRX)
    {
        mDelaymS(1);
        to++;
        if (!forever && (to >= timeout))
        {
            break;
        }
    }

    if (i2c_master_buffer_index < length)
        length = i2c_master_buffer_index;

    // copy i2c buffer to data
    tmos_memcpy(data, i2c_master_buffer, length);

    return length;
}

#ifdef CONFIG_I2C_DEBUG
static void print_i2c_irq_sta(uint32_t state)
{
    I2C_DBG("i2c irq: ( ");

    if (state & RB_I2C_SB)
        I2C_DBG("SB ");
    if (state & RB_I2C_ADDR)
        I2C_DBG("ADDR ");
    if (state & RB_I2C_BTF)
        I2C_DBG("BTF ");
    if (state & RB_I2C_ADD10)
        I2C_DBG("ADD10 ");
    if (state & RB_I2C_STOPF)
        I2C_DBG("STOP ");
    if (state & RB_I2C_RxNE)
        I2C_DBG("RxNE ");
    if (state & RB_I2C_TxE)
        I2C_DBG("TxE ");
    if (state & RB_I2C_BERR)
        I2C_DBG("BERR ");
    if (state & RB_I2C_ARLO)
        I2C_DBG("ARLO ");
    if (state & RB_I2C_AF)
        I2C_DBG("AF ");
    if (state & RB_I2C_OVR)
        I2C_DBG("OVR ");
    if (state & RB_I2C_PECERR)
        I2C_DBG("PECERR ");
    if (state & RB_I2C_TIMEOUT)
        I2C_DBG("TIMEOUT ");
    if (state & RB_I2C_SMBALERT)
        I2C_DBG("SMBALERT ");
    if (state & (RB_I2C_MSL << 16))
        I2C_DBG("MSL ");
    if (state & (RB_I2C_BUSY << 16))
        I2C_DBG("BUSY ");
    if (state & (RB_I2C_TRA << 16))
        I2C_DBG("TRA ");
    if (state & (RB_I2C_GENCALL << 16))
        I2C_DBG("GENCALL ");
    if (state & (RB_I2C_SMBDEFAULT << 16))
        I2C_DBG("SMBDEFAULT ");
    if (state & (RB_I2C_SMBHOST << 16))
        I2C_DBG("SMBHOST ");
    if (state & (RB_I2C_DUALF << 16))
        I2C_DBG("DUALF ");

    I2C_DBG(")");
}
#else
static inline void print_i2c_irq_sta(uint32_t state)
{
    (void)state;
}
#endif

void BSP_I2C_DeInit(void)
{
    GPIOB_SetBits(GPIO_Pin_14);
    GPIOB_ModeCfg(GPIO_Pin_14, GPIO_ModeIN_PU);

    GPIOB_SetBits(GPIO_Pin_15);
    GPIOB_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);
}

// GXHTC3C_CMD_LOW_CLK_STRE_OFF_HUMI_FRONT
void GXHTC3C_SendCmd(uint16_t cmd)
{
    int ret;
    i2c_tx_data[0] = HI_UINT16(cmd);
    i2c_tx_data[1] = LO_UINT16(cmd);
    ret            = I2C_Write(GXHTC3C_ADDR, (const uint8_t *)&i2c_tx_data, 2, true, true);
    // logDebug("GXHTC3C_SendCmd %s", ret ? "failed" : "success");
}

void GXHTC3C_Sleep(void)
{
    GXHTC3C_SendCmd(GXHTC3C_CMD_SLEEP);
}

void GXHTC3C_Wakeup(void)
{
    GXHTC3C_SendCmd(GXHTC3C_CMD_WAKEUP);
}

void GXHTC3C_GetStart(void)
{
    // 低功耗、Clock stretching关闭、湿度在前
    GXHTC3C_SendCmd(GXHTC3C_CMD_LOW_CLK_STRE_OFF_HUMI_FRONT);
}

// 定义CRC-8参数
#define POLYNOMIAL  0x31  // 生成多项式：x8+x5+x4+1 (省略最高位1)
#define INIT_VALUE  0xFF  // 初始化值
#define REFLECT_IN  false // 不反射输入
#define REFLECT_OUT false // 不反射输出

uint8_t GXHTC3C_CRC_8(uint8_t *p_crc, uint8_t len)
{
    uint8_t crc = INIT_VALUE;

    for (size_t i = 0; i < len; ++i)
    {
        crc ^= p_crc[i]; // 异或当前字节

        for (uint8_t j = 0; j < 8; ++j)
        {
            if (crc & 0x80)
            {                                  // 如果最高位是1
                crc = (crc << 1) ^ POLYNOMIAL; // 左移一位并异或多相式
            }
            else
            {
                crc <<= 1; // 否则只左移一位
            }

            // 确保crc保持在8位内
            crc &= 0xFF;
        }
    }

    // 最终返回CRC值
    return crc;
}

// GXHTC3C_CMD_LOW_CLK_STRE_OFF_HUMI_FRONT
uint8_t GXHTC3C_GetTempHumi(float *humi, float *temp)
{
    int ret;
    ret = I2C_Read(GXHTC3C_ADDR, (uint8_t *)&i2c_rx_data, 6, true, 1000);
    logDebug("read %d byte(s) from %#x", ret, GXHTC3C_ADDR);
    if (ret != 6)
    {
        logError("read failed");
        logHexDumpAll(i2c_rx_data, ret);
        return 1;
    }

    // 低功耗、Clock stretching关闭、湿度在前
    uint8_t humi_raw_data[2];
    uint8_t temp_raw_data[2];

    humi_raw_data[0]  = i2c_rx_data[0];
    humi_raw_data[1]  = i2c_rx_data[1];
    uint16_t raw_humi = (humi_raw_data[0] << 8) | humi_raw_data[1];
    uint8_t crc_humi  = GXHTC3C_CRC_8(humi_raw_data, 2);

    temp_raw_data[0]  = i2c_rx_data[3];
    temp_raw_data[1]  = i2c_rx_data[4];
    uint16_t raw_temp = (temp_raw_data[0] << 8) | temp_raw_data[1];
    uint8_t crc_temp  = GXHTC3C_CRC_8(temp_raw_data, 2);

    if ((crc_humi == i2c_rx_data[2]) && (crc_temp == i2c_rx_data[5]))
    {
        // logDebug("crc ok");
        // logHexDumpAll(i2c_rx_data, 6);

        *humi = (100.0 * raw_humi) / 65536.0;        // 湿度真实值
        *temp = (175.0 * raw_temp) / 65536.0 - 45.0; // 温度真实值
        // logDebug("humi %f, temp %f", *humi, *temp);
    }
    else
    {
        logHexDumpAll(i2c_rx_data, 6);
        logError("crc_temp 0x%02x, crc_humi 0x%02x", crc_temp, crc_humi);
        logError("crc error");
        return 2;
    }
    // logDebug("0 success");
    return 0;
}

void GXHTC3C_Read(void)
{
    int ret;

    GXHTC3C_Wakeup();
    DelayMs(20);

    GXHTC3C_GetStart();
    DelayMs(20);
    float humi, temp;
    ret = GXHTC3C_GetTempHumi(&humi, &temp);
    if (ret == 0)
    {
        // logDebug("ret=0");
        logDebug("humi %.2f %, temp %.2f C", humi, temp);
    }

    GXHTC3C_Sleep();
}

void GXHTC3C_Init(void)
{
#if 1
    BSP_I2C_Init(MASTER_ADDR);
    int ret;

    GXHTC3C_Sleep();
    DelayMs(100);

    GXHTC3C_Wakeup();
    DelayMs(20);

    GXHTC3C_GetStart();
    DelayMs(20);
    float humi, temp;
    ret = GXHTC3C_GetTempHumi(&humi, &temp);
    if (ret == 0)
    {
        // logDebug("humi %.2f %, temp %.2f C", humi, temp);
    }

    GXHTC3C_Sleep();

    for (uint8_t i = 0; i < 3; i++)
    {
        GXHTC3C_Read();
    }

#endif
}

#if 1
__INTERRUPT
__HIGH_CODE
void I2C_IRQHandler(void)
{
    uint32_t event = I2C_GetLastEvent();
    print_i2c_irq_sta(event);

    /* I2C Master */
    if (event & (RB_I2C_MSL << 16))
    {
        if (event & RB_I2C_SB)
        {
            /* Start condition sent, send address */
            I2C_SendData(i2c_slave_addr_rw);
            I2C_DBG("Master selected, send address");
        }

        /* I2C Master transmitter */
        if (event & (RB_I2C_TRA << 16))
        {
            I2C_DBG("Master transmitter:");
            /* Slave receiver acked address or sent bit */
            if (event & (RB_I2C_ADDR | RB_I2C_BTF | RB_I2C_TxE | (RB_I2C_TRA << 16)))
            {
                /* if there is data to send, send it, otherwise stop */
                if (i2c_master_buffer_index < i2c_master_buffer_length)
                {
                    I2C_SendData(i2c_master_buffer[i2c_master_buffer_index++]);
                    I2C_DBG("  send (%#x)\n",
                            i2c_master_buffer[i2c_master_buffer_index - 1]);
                }
                else
                {
                    if (i2c_send_stop)
                    {
                        i2c_state = I2C_READY;
                        I2C_GenerateSTOP(ENABLE);
                        I2C_DBG("  send STOP");
                    }
                    else
                    {
                        i2c_in_repstart = true;
                        /* we're gonna send the START, don't enable the interrupt. */
                        I2C_ITConfig(I2C_IT_BUF, DISABLE);
                        I2C_ITConfig(I2C_IT_EVT, DISABLE);
                        I2C_ITConfig(I2C_IT_ERR, DISABLE);
                        I2C_GenerateSTART(ENABLE);
                        i2c_state = I2C_READY;
                        I2C_DBG("  restart");
                    }
                }
            }

            /* Address or data sent, nack received */
            if (event & RB_I2C_AF)
            {
                I2C_ClearFlag(I2C_FLAG_AF);

                i2c_error = I2C_MT_NACK;
                i2c_state = I2C_READY;
                I2C_GenerateSTOP(ENABLE);
                I2C_DBG("  NACK received, sent stop");
            }
        }
        else
        {
            /* I2C Master reveiver */
            I2C_DBG("Master receiver:");

            /* address sent, ack received */
            if (event & RB_I2C_ADDR)
            {
                /* ack if more bytes are expected, otherwise nack */
                if (i2c_master_buffer_length)
                {
                    I2C_AcknowledgeConfig(ENABLE);
                    I2C_DBG("  address sent");
                    I2C_DBG("  ACK next");
                }
                else
                {
                    // XXX: Should not delay too match before NACK
                    I2C_AcknowledgeConfig(DISABLE);
                    is_nack_sent = true;
                    I2C_DBG("  address sent");
                    I2C_DBG("  NACK next");
                }
            }

            /* data reveived */
            if (event & (RB_I2C_RxNE))
            {
                /* put byte into buffer */
                i2c_master_buffer[i2c_master_buffer_index++] = I2C_ReceiveData();

                if (i2c_master_buffer_index < i2c_master_buffer_length)
                {
                    I2C_AcknowledgeConfig(ENABLE);
                    I2C_DBG("  ACK next");
                }
                else
                {
                    // XXX: Should not delay too match before NACK
                    I2C_AcknowledgeConfig(DISABLE);
                    I2C_DBG("  NACK next");

                    if (is_nack_sent)
                    {
                        is_nack_sent = false;
                        if (i2c_send_stop)
                        {
                            I2C_GenerateSTOP(ENABLE);
                            i2c_state = I2C_READY;
                            I2C_DBG("  send STOP");
                        }
                        else
                        {
                            i2c_in_repstart = true;
                            /* we're gonna send the START, don't enable the interrupt. */
                            I2C_ITConfig(I2C_IT_BUF, DISABLE);
                            I2C_ITConfig(I2C_IT_EVT, DISABLE);
                            I2C_ITConfig(I2C_IT_ERR, DISABLE);
                            I2C_GenerateSTART(ENABLE);
                            i2c_state = I2C_READY;
                            I2C_DBG("  restart");
                        }
                    }
                    else
                    {
                        is_nack_sent = true;
                    }
                }

                I2C_DBG("  received data (%#x)\n",
                        i2c_master_buffer[i2c_master_buffer_index - 1]);
            }

            /* nack received */
            if (event & RB_I2C_AF)
            {
                I2C_ClearFlag(I2C_FLAG_AF);
                /* put final byte into buffer */
                i2c_master_buffer[i2c_master_buffer_index++] = I2C_ReceiveData();

                if (i2c_send_stop)
                {
                    i2c_state = I2C_READY;
                    I2C_GenerateSTOP(ENABLE);
                    I2C_DBG("  NACK received, send STOP");
                }
                else
                {
                    i2c_in_repstart = true;
                    /* we're gonna send the START, don't enable the interrupt. */
                    I2C_ITConfig(I2C_IT_BUF, DISABLE);
                    I2C_ITConfig(I2C_IT_EVT, DISABLE);
                    I2C_ITConfig(I2C_IT_ERR, DISABLE);
                    I2C_GenerateSTART(ENABLE);
                    i2c_state = I2C_READY;
                    I2C_DBG("  restart");
                }
            }
        }
    }
    else
    {
        /* I2C slave */
        /* addressed, returned ack */
        if (event & RB_I2C_ADDR)
        {

            if (event & ((RB_I2C_TRA << 16) | RB_I2C_TxE))
            {
                I2C_DBG("Slave transmitter address matched");

                i2c_state                 = I2C_STX;
                i2c_slave_txbuffer_index  = 0;
                i2c_slave_txbuffer_length = 0;

                if (slave_cb && slave_cb->on_transmit)
                {
                    slave_cb->on_transmit(i2c_slave_txbuffer, &i2c_slave_txbuffer_length);
                }
            }
            else
            {
                I2C_DBG("Slave reveiver address matched");

                i2c_state                = I2C_SRX;
                i2c_slave_rxbuffer_index = 0;
            }
        }

        if (event & (RB_I2C_TRA << 16))
        { // TODO: STOP?
            /* Slave transmintter */
            I2C_AcknowledgeConfig(ENABLE);
            I2C_DBG("Slave transmitter:");

            if (event & RB_I2C_AF)
            {
                /* Nack received */
                I2C_ClearFlag(I2C_FLAG_AF);
                I2C_AcknowledgeConfig(ENABLE);
                I2C_DBG("  Nack received");

                /* leave slave receiver state */
                i2c_state = I2C_READY;
                /* clear status */
                event = 0;
            }

            if (event & (RB_I2C_BTF | RB_I2C_TxE))
            {
                /* if there is more to send, ack, otherwise send 0xff */
                if (i2c_slave_txbuffer_index < i2c_slave_txbuffer_length)
                {
                    /* copy data to output register */
                    I2C_SendData(i2c_slave_txbuffer[i2c_slave_txbuffer_index++]);
                    I2C_DBG("  send (%#x)\n",
                            i2c_slave_txbuffer[i2c_slave_txbuffer_index - 1]);
                }
                else
                {
                    I2C_SendData(0xff);
                    I2C_DBG("  no more data, send 0xff");
                }
            }
        }
        else
        {
            /* Slave receiver */
            I2C_DBG("Slave receiver:");

            if (event & RB_I2C_RxNE)
            {
                /* if there is still room in the rx buffer */
                if (i2c_slave_rxbuffer_index < I2C_BUFFER_LENGTH)
                {
                    /* put byte in buffer and ack */
                    i2c_slave_rxbuffer[i2c_slave_rxbuffer_index++] = I2C_ReceiveData();
                    I2C_AcknowledgeConfig(ENABLE);
                    I2C_DBG("  received (%#x)\n",
                            i2c_slave_rxbuffer[i2c_slave_rxbuffer_index - 1]);
                }
                else
                {
                    // otherwise nack
                    I2C_AcknowledgeConfig(DISABLE);
                }
            }

            if (event & RB_I2C_STOPF)
            {
                /* ack future responses and leave slave receiver state */
                R16_I2C_CTRL1 |= RB_I2C_PE; // clear flag

                I2C_DBG("  reveive stop");

                /* callback to user defined callback */
                if (slave_cb && slave_cb->on_receive)
                {
                    slave_cb->on_receive(i2c_slave_rxbuffer, i2c_slave_rxbuffer_index);
                }
                /* since we submit rx buffer , we can reset it */
                i2c_slave_rxbuffer_index = 0;
            }

            if (event & RB_I2C_AF)
            {
                I2C_ClearFlag(I2C_FLAG_AF);

                /* ack future responses */
                I2C_AcknowledgeConfig(ENABLE);
            }
        }
    }

    if (event & RB_I2C_BERR)
    {
        I2C_ClearFlag(RB_I2C_BERR);
        I2C_GenerateSTOP(ENABLE);

        i2c_error = I2C_BUS_ERROR;
        I2C_DBG("RB_I2C_BERR");
    }

    if (event & RB_I2C_ARLO)
    {
        I2C_ClearFlag(RB_I2C_ARLO);

        i2c_error = I2C_ARB_LOST;
        I2C_DBG("RB_I2C_ARLO");
    }

    if (event & RB_I2C_OVR)
    {
        I2C_ClearFlag(RB_I2C_OVR);

        i2c_error = I2C_OVR;
        I2C_DBG("RB_I2C_OVR");
    }

    if (event & RB_I2C_PECERR)
    {
        I2C_ClearFlag(RB_I2C_PECERR);

        i2c_error = I2C_PECERR;
        I2C_DBG("RB_I2C_PECERR");
    }

    if (event & RB_I2C_TIMEOUT)
    {
        I2C_ClearFlag(RB_I2C_TIMEOUT);

        i2c_error = I2C_TIMEOUT;
        I2C_DBG("RB_I2C_TIMEOUT");
    }

    if (event & RB_I2C_SMBALERT)
    {
        I2C_ClearFlag(RB_I2C_SMBALERT);

        i2c_error = I2C_SMBALERT;
        I2C_DBG("RB_I2C_SMBALERT");
    }
}
#endif
