#include "dvp.h"
#include "ch32v30x_dvp.h"
#include "eth_driver.h"
#include "string.h"

__attribute__((aligned(4))) uint8_t DMA_LineBuf0[BYTES_PER_LINE];
__attribute__((aligned(4))) uint8_t DMA_LineBuf1[BYTES_PER_LINE];

volatile uint8_t  Line_Ready_Flag  = 0;
volatile uint8_t *Ready_Line_Ptr   = NULL;
volatile uint32_t current_line_idx = 0;
static   uint32_t frame_count      = 0;

extern u8 socket[];
extern volatile uint32_t sys_tick_ms;

void DVP_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    NVIC_InitTypeDef NVIC_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DVP, ENABLE);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_11;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_3 | GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel                   = DVP_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DVP_Cfg(DVP_DMA_Disable, DVP_FLAG_FIFO_RESET_Enable, DVP_RX_RESET_Enable);
    DVP_Mode(RB_DVP_D8_MOD, Video_Mode);
    DVP->CR0 &= ~(RB_DVP_P_POLAR | RB_DVP_V_POLAR | RB_DVP_H_POLAR);
    DVP->DMA_BUF0 = (uint32_t)DMA_LineBuf0;
    DVP->DMA_BUF1 = (uint32_t)DMA_LineBuf1;
    DVP->ROW_NUM  = 1;
    DVP->COL_NUM  = BYTES_PER_LINE;
    DVP_INTCfg(ENABLE, RB_DVP_IE_STR_FRM | RB_DVP_IE_ROW_DONE);
    DVP_Cfg(DVP_DMA_Enable, DVP_FLAG_FIFO_RESET_Disable, DVP_RX_RESET_Disable);
    DVP->CR0 |= RB_DVP_ENABLE;
}

#define PROTOCOL_HEADER_RESERVE 64

__attribute__((aligned(4))) uint8_t FrameBuffer[SENSOR_HEIGHT][BYTES_PER_LINE];
volatile uint8_t Frame_Ready_Flag = 0;
volatile uint32_t Ready_Frame_Count = 0;

void DVP_Task(void)
{
    if (!Line_Ready_Flag) return;

    NVIC_DisableIRQ(DVP_IRQn);
    uint8_t  *line = (uint8_t *)Ready_Line_Ptr;
    uint32_t  idx  = current_line_idx;
    Line_Ready_Flag = 0;
    NVIC_EnableIRQ(DVP_IRQn);

    if (idx < SENSOR_HEIGHT)
    {
        memcpy(FrameBuffer[idx], line, BYTES_PER_LINE);
        
        if (idx == SENSOR_HEIGHT - 1)
        {
            Frame_Ready_Flag = 1;
            Ready_Frame_Count = frame_count;
        }
    }
}

void DVP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void DVP_IRQHandler(void)
{
    if (DVP->IFR & RB_DVP_IF_STR_FRM)
    {
        DVP->IFR         = RB_DVP_IF_STR_FRM;
        current_line_idx = 0;
        frame_count++;
    }
    if (DVP->IFR & RB_DVP_IF_ROW_DONE)
    {
        DVP->IFR       = RB_DVP_IF_ROW_DONE;
        Ready_Line_Ptr = (DVP->CR1 & RB_DVP_BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1;
        current_line_idx++;
        Line_Ready_Flag = 1;
    }
}
