#include "../../hal.h"
#include "stdint.h"
#include "string.h"

#include "stm32g4xx_hal.h"

#define PORT_NUM        2

uint8_t rcvBuf[PORT_NUM][128];
typedef struct
{
    uint8_t             port;           ///< iic port序号
    bool                busyFlg;
    I2C_HandleTypeDef   *hi2c;          ///< iic句柄

    uint8_t             *rx_buffer;     ///< 接收缓冲区
    uint16_t            buffer_size;    ///< 缓冲区大小
    I2C_ReadMode        read_mode;      ///< 读取模式：轮询或DMA
    volatile bool       dmaRxComplete;  ///< DMA传输完成标志（false-未完成，true-完成）
    volatile bool       dmaTxComplete;  ///< DMA传输完成标志（false-未完成，true-完成）
    uint8_t             transferStep;
    uint32_t            time;           ///< 读取time，用于计算超时
}I2C_Port;


I2C_HandleTypeDef hi2c1;
I2C_HandleTypeDef hi2c3;
DMA_HandleTypeDef hdma_i2c1_rx;
DMA_HandleTypeDef hdma_i2c1_tx;
DMA_HandleTypeDef hdma_i2c3_rx;
DMA_HandleTypeDef hdma_i2c3_tx;

static I2C_Port i2cPort[PORT_NUM];

extern void Error_Handler(void);

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.Timing = 0x40621236;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Analogue filter
  */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Digital filter
  */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @brief I2C3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C3_Init(void)
{

  /* USER CODE BEGIN I2C3_Init 0 */

  /* USER CODE END I2C3_Init 0 */

  /* USER CODE BEGIN I2C3_Init 1 */

  /* USER CODE END I2C3_Init 1 */
  hi2c3.Instance = I2C3;
  hi2c3.Init.Timing = 0x40621236;
  hi2c3.Init.OwnAddress1 = 0;
  hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c3.Init.OwnAddress2 = 0;
  hi2c3.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c3) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Analogue filter
  */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c3, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Digital filter
  */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c3, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C3_Init 2 */

  /* USER CODE END I2C3_Init 2 */

}


static void MX_DMA_I2C1_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);

}

static void MX_DMA_I2C3_Init(void)
{

  /* DMA controller clock enable */
  //__HAL_RCC_DMAMUX1_CLK_ENABLE();
  //__HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel3_IRQn interrupt configuration */
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
  /* DMA1_Channel6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);

}




// DMA 完成回调函数
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
    for(uint8_t i=0; i<PORT_NUM; i++)
    {
        if(i2cPort[i].hi2c == hi2c)
        {
            i2cPort[i].dmaRxComplete = 1; // DMA 完成标志置为 1
            break;
        }
    }
}

uint32_t errCode = 0;
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {
    for(uint8_t i=0; i<PORT_NUM; i++)
    {
        if(i2cPort[i].hi2c == hi2c)
        {
            errCode = HAL_I2C_GetError(hi2c);
            i2cPort[i].dmaTxComplete = 1; // DMA 完成标志置为 1
            break;
        }
    }
}

static I2C_Transfer_Status iicWrite(uint8_t port, uint32_t addr, uint8_t *data, uint16_t size)
{
    I2C_Transfer_Status err = I2C_TRANSFER_BUSY;
    I2C_Port *pPort = &i2cPort[port];
    HAL_StatusTypeDef status;
    
    switch(pPort->transferStep)
    {
    case 0:
        if(pPort->busyFlg == false)
        {
            pPort->busyFlg = true;
            pPort->transferStep = 1;
        }
        else
        {
            break;
        }
    case 1:
        if(pPort->read_mode == I2C_MODE_POLLING)
        {
            /// todo 添加轮询读取
            status = HAL_I2C_Master_Transmit(pPort->hi2c, addr << 1, data, size, 200);
            if(status == HAL_OK)
            {
                pPort->time = HAL_GetTick();
                pPort->transferStep = 2;
                pPort->dmaTxComplete = true;
            }
            else
            {
                HAL_I2C_DeInit(pPort->hi2c);  // 反初始化 I2C 外设
                HAL_I2C_Init(pPort->hi2c);    // 重新初始化 I2C 外设
                
                err = I2C_TRANSFER_ERROR;
                pPort->transferStep = 0;
                break;
            }
        }
        else
        {

            pPort->dmaTxComplete = false;

            status = HAL_I2C_Master_Transmit_DMA(pPort->hi2c, addr << 1, data, size);
            if(status == HAL_OK)
            {
                pPort->time = HAL_GetTick();
                pPort->transferStep = 2;
            }
            else
            {
                HAL_I2C_DeInit(pPort->hi2c);  // 反初始化 I2C 外设
                HAL_I2C_Init(pPort->hi2c);    // 重新初始化 I2C 外设
                
                err = I2C_TRANSFER_ERROR;
                pPort->transferStep = 0;
                break;
            }
        }
        
    case 2:
        if(pPort->dmaTxComplete == true)
        {
            pPort->dmaTxComplete = false;
            
            err = I2C_TRANSFER_DONE;

            pPort->transferStep = 0;
        }
        else
        {
            if(HAL_GetTick() > (100 + pPort->time))
            {
                err = I2C_TRANSFER_ERROR;
                pPort->transferStep = 0;
            }
        }
        
        break;
    default:
        pPort->transferStep = 0;
        err = I2C_TRANSFER_ERROR;
        break;
    }

    if((err == I2C_TRANSFER_DONE) || (err == I2C_TRANSFER_ERROR))
    {
        pPort->busyFlg = false;
    }
    
    return err;
}

static I2C_Transfer_Status iicRead(uint8_t port, uint32_t addr, uint8_t *data, uint16_t size)
{
    I2C_Transfer_Status err = I2C_TRANSFER_BUSY;
    I2C_Port *pPort = &i2cPort[port];
    HAL_StatusTypeDef status;
    
    switch(pPort->transferStep)
    {
    case 0:
        if(pPort->busyFlg == false)
        {
            pPort->busyFlg = true;
            pPort->transferStep = 1;
        }
        else
        {
            break;
        }
    case 1:
        if(pPort->read_mode == I2C_MODE_POLLING)
        {
            /// todo 添加轮询读取
            status = HAL_I2C_Master_Receive(pPort->hi2c, addr << 1, pPort->rx_buffer, size, 200);
            if(status == HAL_OK)
            {
                pPort->time = HAL_GetTick();
                pPort->transferStep = 2;

                pPort->dmaRxComplete = true;
            }
            else
            {
                err = I2C_TRANSFER_ERROR;
                pPort->transferStep = 0;
                break;
            }
        }
        else
        {
            status = HAL_I2C_Master_Receive_DMA(pPort->hi2c, addr << 1, pPort->rx_buffer, size);
            if(status == HAL_OK)
            {
                pPort->time = HAL_GetTick();
                pPort->transferStep = 2;
            }
            else
            {
                err = I2C_TRANSFER_ERROR;
                pPort->transferStep = 0;
                break;
            }
        }
        
    case 2:
        if(pPort->dmaRxComplete == true)
        {
            pPort->dmaRxComplete = false;
            
            err = I2C_TRANSFER_DONE;
            memcpy((void *)data, pPort->rx_buffer, size);

            pPort->transferStep = 0;
        }
        else
        {
            if(HAL_GetTick() > (100 + pPort->time))
            {
                err = I2C_TRANSFER_ERROR;
                pPort->transferStep = 0;
            }
        }
        
        break;
    default:
        err = I2C_TRANSFER_ERROR;
        pPort->transferStep = 0;
        break;
    }

    if((err == I2C_TRANSFER_DONE) || (err == I2C_TRANSFER_ERROR))
    {
        pPort->busyFlg = false;
    }

    return err;
}

bool I2C_Init(uint8_t port, I2C_PlatformInterface_t *com)
{
    /// todo 添加port合法性判断
    memset((void *)&i2cPort[port], 0, sizeof(I2C_Port));
    
    if(port == 0)
    {
        i2cPort[port].hi2c = &hi2c1;
        MX_DMA_I2C1_Init();
        MX_I2C1_Init();
    }
    else
    {
        i2cPort[port].hi2c = &hi2c3;

        MX_DMA_I2C3_Init();
        MX_I2C3_Init();
    }

    memset((void *)&rcvBuf[port], 0, sizeof(sizeof(rcvBuf)/PORT_NUM));

    com->read = iicRead;
    com->write = iicWrite;
    com->delayMs = HAL_Delay;
    com->getTimeMs = HAL_GetTick;
    com->port = port;

    i2cPort[port].read_mode = I2C_MODE_POLLING;
    i2cPort[port].port = port;
    i2cPort[port].buffer_size = sizeof(rcvBuf)/PORT_NUM;
    i2cPort[port].rx_buffer = (uint8_t *)&rcvBuf[port];
    
    return true;
}


