/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include "adc_drv.h"
#include "adc_reg.h"
#include "scg0_reg.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/
#define ADC_LEN_PWR_ON_DEFAULT   (6U)
#define ADC_LEN_SMPL_1ST_DEFAULT (8U)
#define ADC_LEN_SMPL_DEFAULT     (0U)
#define SCG0_REG_LOCK_KEY        (0x0U)
#define SCG0_REG_UNLOCK_KEY      (0x19962020U)
/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*! \brief ADC type of enable selection
 */
typedef enum
{
    ADCDRV_POWER_ON   = 0x0U,
    ADCDRV_POWER_DOWN = 0x1U,
} AdcDrv_PowerEnable;

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the functions
 ******************************************************************************/

void AdcDrv_SetArb(AdcDrvType *obj,const AdcDrv_ArbType arbType)
{
    while(AdcReg_GetGlo2ArbChange(obj->adcReg) == 0)
    {
        ;
    }
    AdcReg_SetGlo2Arb(obj->adcReg, (uint8_t)arbType);
}

void AdcDrv_Init(AdcDrvType *obj, struct _AdcRegType_ *adcReg, struct _Scg0RegType_ *scg0Reg)
{
    obj->adcReg  = adcReg;
    obj->scg0Reg = scg0Reg;
}

void AdcDrv_GetCmpCfg(AdcDrvType *obj, AdcDrv_CmpCfgType *currentConfig)
{
    bool     acfe  = AdcReg_GetSc2Acfe(obj->adcReg);
    bool     acfgt = AdcReg_GetSc2AcFgt(obj->adcReg);
    bool     acren = AdcReg_GetSc2AcREn(obj->adcReg);
    uint16_t cv1   = AdcReg_GetCv1Cv(obj->adcReg);
    uint16_t cv2   = AdcReg_GetCv2Cv(obj->adcReg);
    if(acfe == false)
    {
        currentConfig->cmpMode  = ADCDRV_CMPMODE_NO_COMPARE;
        currentConfig->lowerLim = 0;
        currentConfig->upperLim = 0;
    }
    else
    {
        if(acfgt == false && acren == false)
        {
            currentConfig->cmpMode  = ADCDRV_CMPMODE_LESSTHAN;
            currentConfig->lowerLim = cv1;
            currentConfig->upperLim = 0;
        }
        else if(acfgt == true && acren == false)
        {
            currentConfig->cmpMode  = ADCDRV_CMPMODE_GREATER_OR_EQUTHAN;
            currentConfig->lowerLim = cv1;
            currentConfig->upperLim = 0;
        }
        else if(acfgt == false && acren == true && cv1 <= cv2)
        {
            currentConfig->cmpMode  = ADCDRV_CMPMODE_OUTSIDE_EXCLUDE_BOUNDARIES;
            currentConfig->lowerLim = cv1;
            currentConfig->upperLim = cv2;
        }
        else if(acfgt == false && acren == true && cv1 > cv2)
        {
            currentConfig->cmpMode  = ADCDRV_CMPMODE_INSIDE_EXCLUDE_BOUNDARIES;
            currentConfig->lowerLim = cv2;
            currentConfig->upperLim = cv1;
        }
        else if(acfgt == true && acren == true && cv1 <= cv2)
        {
            currentConfig->cmpMode  = ADCDRV_CMPMODE_INSIDE_INCLUDE_BOUNDARIES;
            currentConfig->lowerLim = cv1;
            currentConfig->upperLim = cv2;
        }
        else if(acfgt == true && acren == true && cv1 > cv2)
        {
            currentConfig->cmpMode  = ADCDRV_CMPMODE_OUTSIDE_INCLUDE_BOUNDARIES;
            currentConfig->lowerLim = cv2;
            currentConfig->upperLim = cv1;
        }
    }
}

void AdcDrv_CfgCompare(AdcDrvType *obj, const AdcDrv_CmpCfgType *userConfig)
{
    switch(userConfig->cmpMode)
    {
    case(ADCDRV_CMPMODE_NO_COMPARE):
        AdcReg_SetSc2Acfe(obj->adcReg, false);
        break;
    case(ADCDRV_CMPMODE_LESSTHAN):
        AdcReg_SetSc2Acfe(obj->adcReg, true);
        AdcReg_SetSc2AcFgt(obj->adcReg, false);
        AdcReg_SetSc2AcREn(obj->adcReg, false);
        AdcReg_SetCv1Cv(obj->adcReg, userConfig->lowerLim);
        AdcReg_SetCv2Cv(obj->adcReg, 0u);
        break;
    case(ADCDRV_CMPMODE_GREATER_OR_EQUTHAN):
        AdcReg_SetSc2Acfe(obj->adcReg, true);
        AdcReg_SetSc2AcFgt(obj->adcReg, true);
        AdcReg_SetSc2AcREn(obj->adcReg, false);
        AdcReg_SetCv1Cv(obj->adcReg, userConfig->lowerLim);
        AdcReg_SetCv2Cv(obj->adcReg, 0u);
        break;
    case(ADCDRV_CMPMODE_OUTSIDE_EXCLUDE_BOUNDARIES):
        AdcReg_SetSc2Acfe(obj->adcReg, true);
        AdcReg_SetSc2AcFgt(obj->adcReg, false);
        AdcReg_SetSc2AcREn(obj->adcReg, true);
        AdcReg_SetCv1Cv(obj->adcReg, userConfig->lowerLim);
        AdcReg_SetCv2Cv(obj->adcReg, userConfig->upperLim);
        break;
    case(ADCDRV_CMPMODE_INSIDE_EXCLUDE_BOUNDARIES):
        AdcReg_SetSc2Acfe(obj->adcReg, true);
        AdcReg_SetSc2AcFgt(obj->adcReg, false);
        AdcReg_SetSc2AcREn(obj->adcReg, true);
        AdcReg_SetCv1Cv(obj->adcReg, userConfig->upperLim);
        AdcReg_SetCv2Cv(obj->adcReg, userConfig->lowerLim);
        break;
    case(ADCDRV_CMPMODE_INSIDE_INCLUDE_BOUNDARIES):
        AdcReg_SetSc2Acfe(obj->adcReg, true);
        AdcReg_SetSc2AcFgt(obj->adcReg, true);
        AdcReg_SetSc2AcREn(obj->adcReg, true);
        AdcReg_SetCv1Cv(obj->adcReg, userConfig->lowerLim);
        AdcReg_SetCv2Cv(obj->adcReg, userConfig->upperLim);
        break;
    case(ADCDRV_CMPMODE_OUTSIDE_INCLUDE_BOUNDARIES):
        AdcReg_SetSc2Acfe(obj->adcReg, true);
        AdcReg_SetSc2AcFgt(obj->adcReg, true);
        AdcReg_SetSc2AcREn(obj->adcReg, true);
        AdcReg_SetCv1Cv(obj->adcReg, userConfig->upperLim);
        AdcReg_SetCv2Cv(obj->adcReg, userConfig->lowerLim);
        break;
    default:
        /* Impossible case */
        break;
    }
}

void AdcDrv_Configure(AdcDrvType *obj, const AdcDrv_ConfigType *userConfig)
{
    /* disable ADC first */
    AdcReg_SetSc3AdcPD(obj->adcReg, ADCDRV_POWER_DOWN);

    /* select the conversion conversionMode */
    AdcReg_SetCfg1Mode(obj->adcReg, (uint8_t)userConfig->conversionMode);

    /* select the ADC sample length */
    AdcReg_SetCfg2LenSmpl(obj->adcReg, userConfig->sampleLen);

    /* select the ADC sample length after the 1st cycle
     * change the input channel select(ADCH) */
    AdcReg_SetCfg2LenSmpl1st(obj->adcReg, userConfig->firstSampleLen);

    /* select the ADC sample length after power on */
    AdcReg_SetGlo2LenPwrOn(obj->adcReg, userConfig->pwrOnLen);

    /* enable DMA */
    AdcReg_SetSc2DmaEn(obj->adcReg, userConfig->dmaEn);

    /* Enable trigger */
#if FEATURE_ADC_SWTRG_ON == 1
    AdcReg_SetSc2AdTrg(obj->adcReg, userConfig->trgSrcCfg.trgSrc);
#else
    AdcReg_SetSc2AdTrg(obj->adcReg, 1);
#endif
    /* unlock scg0 */
    Scg0Reg_SetKey(obj->scg0Reg, SCG0_REG_UNLOCK_KEY);

    /* select hardware trigger source */
    Scg0Reg_SetAdcTrgSrc(obj->scg0Reg, (uint8_t)userConfig->trgSrcCfg.hwTrgSrc);

    /* select software pretrigger source */
    Scg0Reg_SetAdcPreTrgSwSrc(obj->scg0Reg, (uint8_t)userConfig->trgSrcCfg.swPretrgSrc);

    /* set pretrigger source */
    Scg0Reg_SetAdcPreTrgSrc(obj->scg0Reg, (uint8_t)userConfig->trgSrcCfg.pretrgSrc);

    /* lock scg0 */
    Scg0Reg_SetKey(obj->scg0Reg, SCG0_REG_LOCK_KEY);

    /* select the continuous / one shot conversion */
    AdcReg_SetSc3AdCo(obj->adcReg, (uint8_t)userConfig->continuousMode);

    /* configure the ADC compare function */
    AdcDrv_CfgCompare(obj, &(userConfig->cmpCfg));

    /* enable hardware average sample */
    AdcReg_SetSc3AvgE(obj->adcReg, userConfig->avgEnable);
    if(userConfig->avgEnable)
    {
        /* select the hardware average sample number */
        AdcReg_SetSc3AvgS(obj->adcReg, (uint8_t)userConfig->avgSamplesSel);
    }

    /* configure for each ADC channel */
    uint8_t i;
    for(i = 0; i < ADCDRV_CHANNEL_NUM; i++)
    {
        /* select the ADC channel input */
        AdcReg_SetScPch(obj->adcReg, i, (uint8_t)userConfig->chnCfg[i].pCh);

        /* enable the interrupt for ADC channel */
        AdcReg_SetScAIEn(obj->adcReg, i, userConfig->chnCfg[i].intEnable);
    }

    /*configure the priority of general ADC and touch request*/
    AdcDrv_SetArb(obj, userConfig->arbType);

    /* enable ADC */
    AdcReg_SetSc3AdcPD(obj->adcReg, ADCDRV_POWER_ON);
}

void AdcDrv_GetDefaultConfig(AdcDrv_ConfigType *userConfig)
{
    uint8_t i = 0U;

    userConfig->conversionMode   = ADCDRV_CONVERSION_10BIT;
    userConfig->sampleLen        = ADC_LEN_SMPL_DEFAULT;
    userConfig->firstSampleLen   = ADC_LEN_SMPL_1ST_DEFAULT;
    userConfig->pwrOnLen         = ADC_LEN_PWR_ON_DEFAULT;
    userConfig->dmaEn            = false;
    userConfig->cmpCfg.lowerLim  = 0;
    userConfig->cmpCfg.upperLim  = 0;
    userConfig->cmpCfg.cmpMode   = ADCDRV_CMPMODE_NO_COMPARE;
    userConfig->continuousMode   = ADCDRV_ONESHOT;
    userConfig->avgSamplesSel    = ADCDRV_AVERAGE_4;
    userConfig->avgEnable        = true;
    userConfig->arbType          = ADCDRV_ARBTYPE_GENERAL_PRIORITY;
#if FEATURE_ADC_SWTRG_ON == 1
    userConfig->trgSrcCfg.trgSrc = ADCDRV_HW_TRIGGER;
#endif
    userConfig->trgSrcCfg.hwTrgSrc    = ADCDRV_HW_TRGSRC_PDB;
    userConfig->trgSrcCfg.swPretrgSrc = ADCDRV_SWPRETRG_PRETRIGGER_0;
    userConfig->trgSrcCfg.pretrgSrc   = ADCDRV_PRETRG_PDB_PRETRIGGER;

    for(i = 0; i < ADCDRV_CHANNEL_NUM; i++)
    {
        userConfig->chnCfg[i].pCh       = (AdcDrv_PchType)i;
        userConfig->chnCfg[i].intEnable = false;
    }
}

void AdcDrv_ClrLockTrg(AdcDrvType *obj)
{
    AdcReg_ClrlTrg(obj->adcReg);
}

uint16_t AdcDrv_GetRn(AdcDrvType *obj, uint8_t chn)
{
    return (AdcReg_GetRData(obj->adcReg, (uint8_t)chn));
}

bool AdcDrv_GetCocon(AdcDrvType *obj, uint8_t chn)
{
    if(AdcReg_GetScCoCo(obj->adcReg, (uint8_t)chn) > 0)
    {
        return (true);
    }
    else
    {
        return (false);
    }
}

void AdcDrv_DisableAdc(AdcDrvType *obj)
{
    AdcReg_SetSc3AdcPD(obj->adcReg, ADCDRV_POWER_DOWN);
}

void AdcDrv_EnableAdc(AdcDrvType *obj)
{
    AdcReg_SetSc3AdcPD(obj->adcReg, ADCDRV_POWER_ON);
}

void AdcDrv_AbortChn(AdcDrvType *obj, uint8_t chn)
{
    AdcReg_AbortChn(obj->adcReg, chn);
}

void AdcDrv_ConfigurePchn(AdcDrvType *obj, uint8_t chn, AdcDrv_PchType state)
{
    AdcReg_SetScPch(obj->adcReg, chn, (uint8_t)state);
}

void AdcDrv_GetConfig(AdcDrvType *obj, AdcDrv_ConfigType *currentConfig)
{
    /* get the conversion conversionMode */
    currentConfig->conversionMode = (AdcDrv_ConversionMode)AdcReg_GetCfg1Mode(obj->adcReg);

    /* get the ADC sample length */
    currentConfig->sampleLen = AdcReg_GetCfg2LenSmpl(obj->adcReg);

    /* get the ADC sample length after the 1st cycle
     * change the input channel select(ADCH) */
    currentConfig->firstSampleLen = AdcReg_GetCfg2LenSmpl1st(obj->adcReg);

    /* get the ADC sample length after power on */
    currentConfig->pwrOnLen = AdcReg_GetGlo2LenPwrOn(obj->adcReg);

    /* get enable DMA */
    currentConfig->dmaEn = AdcReg_GetSc2DmaEn(obj->adcReg);

#if FEATURE_ADC_SWTRG_ON == 1
    currentConfig->trgSrcCfg.trgSrc = (AdcDrv_TrgMode)AdcReg_GetSc2AdTrg(obj->adcReg);
#endif

    /* get hardware trigger source */
    currentConfig->trgSrcCfg.hwTrgSrc = (AdcDrv_HwTrgSrc)Scg0Reg_GetAdcTrgSrc(obj->scg0Reg);

    /* get software pretrigger source */
    currentConfig->trgSrcCfg.swPretrgSrc = (ADCDRV_SwPretrgSrc)Scg0Reg_GetAdcPreTrgSwSrc(obj->scg0Reg);

    /* get pretrigger source */
    currentConfig->trgSrcCfg.pretrgSrc = (ADCDRV_PretrgSrc)Scg0Reg_GetAdcPreTrgSrc(obj->scg0Reg);

    /* get configuration the ADC compare function */
    AdcDrv_GetCmpCfg(obj, &currentConfig->cmpCfg);

    /* get hardware average sample configuration*/
    currentConfig->avgEnable = AdcReg_GetSc3AvgE(obj->adcReg);

    /* get the hardware average sample number */
    currentConfig->avgSamplesSel = (AdcDrv_AverageMode)AdcReg_GetSc3AvgS(obj->adcReg);

    /* get configuration for each ADC channel */
    uint8_t i;
    for(i = 0; i < ADCDRV_CHANNEL_NUM; i++)
    {
        /* get the ADC channel input */
        currentConfig->chnCfg[i].pCh = (AdcDrv_PchType)AdcReg_GetScPch(obj->adcReg, i);

        /* get the interrupt for ADC channel state*/
        currentConfig->chnCfg[i].intEnable = AdcReg_GetScAIEn(obj->adcReg, i);
    }
}

uint8_t AdcDrv_GetTrgErr(AdcDrvType *obj)
{
    return(AdcReg_GetSc2TrgStErr(obj->adcReg));
}

void AdcDrv_ClrTrgErr(AdcDrvType *obj)
{
    AdcReg_ClrSc2TrgStErr(obj->adcReg);
}

uint8_t AdcDrv_GetTrgSt(AdcDrvType *obj)
{
    return(AdcReg_GetSc2TrgStLat(obj->adcReg));
}

uint8_t AdcDrv_GetTrgPrNum(AdcDrvType *obj)
{
    return(AdcReg_GetSc2TrgPrNum(obj->adcReg));
}

#if FEATURE_ADC_SWTRG_ON == 1
void AdcDrv_SwTrg(AdcDrvType *obj)
{
    AdcReg_SwTrig(obj->adcReg);
}
#endif
