/**
 * @file        camera.c
 *
 * @brief       This file provides application support for camera
 *
 * @version     V1.0.0
 *
 * @date        2023-07-31
 *
 * @attention
 *
 *  Copyright (C) 2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes ***************************************************************/
#include "camera.h"

/* Private includes *******************************************************/
#include "cameraif.h"
#include "apm32f4xx_device_cfg.h"

/* Private macro **********************************************************/

/* Camera data buffer size*/
#define CAMERA_BUF_SIZE         10U * 1024U

/* Private typedef ********************************************************/

/* Private variables ******************************************************/
__IO uint32_t jpegFrameNum = 0U;
__IO uint32_t jpegFlag = 0U;
__IO uint32_t jpegFrameLen;

/* Count the data lenght of jpeg */
volatile uint32_t jpgLenght = 0;
/* Flag the start position of jpeg data */
volatile uint32_t jpgStartFlag = 0;
/* Flag the data head of jpeg */
volatile uint8_t headFlag = 0;

/* Camera data buffer*/
uint32_t cameraBuffer[CAMERA_BUF_SIZE];

/* Private function prototypes ********************************************/

/* External variables *****************************************************/
extern DCI_HandleTypeDef hdci1;
extern UART_HandleTypeDef huart1;

/* External functions *****************************************************/

/**
 * @brief   Init camera
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_Init(void)
{
    CAMERA_Config(CAMERA_IMG_J320x240);
}

/**
 * @brief   Starts camera capture in continuous mode
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_StartContinuousCapture(void)
{
    __DAL_DCI_ENABLE_IT(&hdci1, DCI_IT_FRAME);
    
    DAL_DCI_Start_DMA(&hdci1, DCI_MODE_CONTINUOUS, \
                     (uint32_t)&cameraBuffer, CAMERA_BUF_SIZE);
}

/**
 * @brief   Starts camera capture in snapshot mode
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_StartSnapshotCapture(void)
{
    DAL_DCI_Start_DMA(&hdci1, DCI_MODE_SNAPSHOT, \
                     (uint32_t)&cameraBuffer, CAMERA_BUF_SIZE);
}

/**
 * @brief   Suspends camera capture
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_SuspendCapture(void)
{
    DAL_DCI_Suspend(&hdci1);
}

/**
 * @brief   Resumes camera capture
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_ResumesCapture(void)
{
    DAL_DCI_Resume(&hdci1);
}

/**
 * @brief   Stops camera capture
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_StopCapture(void)
{
    DAL_DCI_Stop(&hdci1);
}

/**
 * @brief   Display jpeg
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_JpegDisplay(void)
{
    static uint8_t *p = NULL;
    static uint32_t i;
    
    if (jpegFlag == 1)
    {
        p = (uint8_t*)cameraBuffer;
        
        /* DMA memory size is 4 bytes */
        for(i = 0; i < jpegFrameLen * 4; i++)
        {
            /* Find the head of data */
            if((p[i] == 0xFF) && (p[i+1] == 0xD8))
            {
                /* Record the position of jpeg head */
                jpgStartFlag = i;

                headFlag = 1;
            }
            /* Find the end of data */
            if((p[i] == 0xFF) && (p[i+1] == 0xD9) && headFlag)
            {
                /* Avoid some error image data */
                if(jpgStartFlag < i)
                {
                    jpgLenght = i - jpgStartFlag + 2;
                }
                else
                {
                    jpgLenght = 0;
                }
                break;
            }
        }
        
        /* Get right jpeg image */
        if(jpgLenght)
        {
            /* Shift to the data head of jpeg */
            p += jpgStartFlag;
            for(i = 0; i < jpgLenght; i++)
            {
                DAL_UART_Transmit(&huart1, (uint8_t *)&p[i], 1, 1000);
            }
        }

        jpegFlag = 2;
        /* Restart DCI DMA */
        Camera_JpegDataProcess();
    }
}

/**
 * @brief   Process jpeg data
 *
 * @param   None
 *
 * @retval  None
 */
void Camera_JpegDataProcess(void)
{
    BOARD_LED_Toggle(LED2);

    if (jpegFlag == 0)
    {
        __DAL_DMA_DISABLE(hdci1.DMA_Handle);
        while(DMA2_Stream2->SCFG & 0x01);
        jpegFrameLen = CAMERA_BUF_SIZE - __DAL_DMA_GET_COUNTER(hdci1.DMA_Handle);
        jpegFlag = 1;
    }
    
    if (jpegFlag == 2)
    {
        __DAL_DCI_ENABLE_IT(&hdci1, DCI_IT_FRAME);
        __DAL_DMA_SET_COUNTER(hdci1.DMA_Handle, CAMERA_BUF_SIZE);
        __DAL_DMA_ENABLE(hdci1.DMA_Handle);

        jpegFlag = 0;
    }

}

/**
 * @brief  Line Event callback
 *
 * @param  hdci pointer to a DCI_HandleTypeDef structure that contains
 *                the configuration information for DCI
 *
 * @retval None
 */
void DAL_DCI_LineEventCallback(DCI_HandleTypeDef *hdci)
{
    
}

/**
 * @brief  VSYNC Event callback
 *
 * @param  hdci pointer to a DCI_HandleTypeDef structure that contains
 *                the configuration information for DCI
 *
 * @retval None
 */
void DAL_DCI_VsyncEventCallback(DCI_HandleTypeDef *hdci)
{
    
}

/**
 * @brief  Frame Event callback
 *
 * @param  hdci pointer to a DCI_HandleTypeDef structure that contains
 *                the configuration information for DCI
 *
 * @retval None
 */
void DAL_DCI_FrameEventCallback(DCI_HandleTypeDef *hdci)
{
    __DAL_DCI_CLEAR_FLAG(hdci, DCI_FLAG_FRAMERI);
    
    jpegFrameNum++;
    
    Camera_JpegDataProcess();
}

/**
 * @brief  Error DCI callback
 *
 * @param  hdci pointer to a DCI_HandleTypeDef structure that contains
 *                the configuration information for DCI
 *
 * @retval None
 */
void DAL_DCI_ErrorCallback(DCI_HandleTypeDef *hdci)
{
    
}
