#include "AD7689.h"
#include "blackfin.h"
#include "clogger.h"
#include "common.h"
#include "commonServerFunctions.h" // blackfin.h, ansi.h

/* AD7689 ADC DEFINES */

/** Read back CFG Register */
#define AD7689_CFG_RB_OFST (0)
#define AD7689_CFG_RB_MSK  (0x00000001 << AD7689_CFG_RB_OFST)

/** Channel sequencer */
#define AD7689_CFG_SEQ_OFST (1)
#define AD7689_CFG_SEQ_MSK  (0x00000003 << AD7689_CFG_SEQ_OFST)
#define AD7689_CFG_SEQ_DSBLE_VAL                                               \
    ((0x0 << AD7689_CFG_SEQ_OFST) & AD7689_CFG_SEQ_MSK)
#define AD7689_CFG_SEQ_UPDTE_DRNG_SQNCE_VAL                                    \
    ((0x1 << AD7689_CFG_SEQ_OFST) & AD7689_CFG_SEQ_MSK)
#define AD7689_CFG_SEQ_SCN_WTH_TMP_VAL                                         \
    ((0x2 << AD7689_CFG_SEQ_OFST) & AD7689_CFG_SEQ_MSK)
#define AD7689_CFG_SEQ_SCN_WTHT_TMP_VAL                                        \
    ((0x3 << AD7689_CFG_SEQ_OFST) & AD7689_CFG_SEQ_MSK)

/** Reference/ buffer selection */
#define AD7689_CFG_REF_OFST (3)
#define AD7689_CFG_REF_MSK  (0x00000007 << AD7689_CFG_REF_OFST)
/** Internal reference. REF = 2.5V buffered output. Temperature sensor enabled.
 */
#define AD7689_CFG_REF_INT_2500MV_VAL                                          \
    ((0x0 << AD7689_CFG_REF_OFST) & AD7689_CFG_REF_OFST)
/** Internal reference. REF = 4.096V buffered output. Temperature sensor
 * enabled. */
#define AD7689_CFG_REF_INT_4096MV_VAL                                          \
    ((0x1 << AD7689_CFG_REF_OFST) & AD7689_CFG_REF_MSK)
/** External reference. Temperature sensor enabled. Internal buffer disabled. */
#define AD7689_CFG_REF_EXT_TMP_VAL                                             \
    ((0x2 << AD7689_CFG_REF_OFST) & AD7689_CFG_REF_MSK)
/** External reference. Temperature sensor enabled. Internal buffer enabled. */
#define AD7689_CFG_REF_EXT_TMP_INTBUF_VAL                                      \
    ((0x3 << AD7689_CFG_REF_OFST) & AD7689_CFG_REF_MSK)
/** External reference. Temperature sensor disabled. Internal buffer disabled.
 */
#define AD7689_CFG_REF_EXT_VAL                                                 \
    ((0x6 << AD7689_CFG_REF_OFST) & AD7689_CFG_REF_MSK)
/** External reference. Temperature sensor disabled. Internal buffer enabled. */
#define AD7689_CFG_REF_EXT_INTBUF_VAL                                          \
    ((0x7 << AD7689_CFG_REF_OFST) & AD7689_CFG_REF_MSK)

/** bandwidth of low pass filter */
#define AD7689_CFG_BW_OFST (6)
#define AD7689_CFG_BW_MSK  (0x00000001 << AD7689_CFG_REF_OFST)
#define AD7689_CFG_BW_ONE_FOURTH_VAL                                           \
    ((0x0 << AD7689_CFG_BW_OFST) & AD7689_CFG_BW_MSK)
#define AD7689_CFG_BW_FULL_VAL ((0x1 << AD7689_CFG_BW_OFST) & AD7689_CFG_BW_MSK)

/** input channel selection IN0 - IN7 */
#define AD7689_CFG_IN_OFST (7)
#define AD7689_CFG_IN_MSK  (0x00000007 << AD7689_CFG_IN_OFST)

/** input channel configuration */
#define AD7689_CFG_INCC_OFST (10)
#define AD7689_CFG_INCC_MSK  (0x00000007 << AD7689_CFG_INCC_OFST)
#define AD7689_CFG_INCC_BPLR_DFFRNTL_PRS_VAL                                   \
    ((0x0 << AD7689_CFG_INCC_OFST) & AD7689_CFG_INCC_MSK)
#define AD7689_CFG_INCC_BPLR_IN_COM_VAL                                        \
    ((0x2 << AD7689_CFG_INCC_OFST) & AD7689_CFG_INCC_MSK)
#define AD7689_CFG_INCC_TMP_VAL                                                \
    ((0x3 << AD7689_CFG_INCC_OFST) & AD7689_CFG_INCC_MSK)
#define AD7689_CFG_INCC_UNPLR_DFFRNTL_PRS_VAL                                  \
    ((0x4 << AD7689_CFG_INCC_OFST) & AD7689_CFG_INCC_MSK)
#define AD7689_CFG_INCC_UNPLR_IN_COM_VAL                                       \
    ((0x6 << AD7689_CFG_INCC_OFST) & AD7689_CFG_INCC_MSK)
#define AD7689_CFG_INCC_UNPLR_IN_GND_VAL                                       \
    ((0x7 << AD7689_CFG_INCC_OFST) & AD7689_CFG_INCC_MSK)

/** configuration update */
#define AD7689_CFG_CFG_OFST (13)
#define AD7689_CFG_CFG_MSK  (0x00000001 << AD7689_CFG_CFG_OFST)
#define AD7689_CFG_CFG_NO_UPDATE_VAL                                           \
    ((0x0 << AD7689_CFG_CFG_OFST) & AD7689_CFG_CFG_MSK)
#define AD7689_CFG_CFG_OVRWRTE_VAL                                             \
    ((0x1 << AD7689_CFG_CFG_OFST) & AD7689_CFG_CFG_MSK)

#define AD7689_ADC_CFG_NUMBITS         (14)
#define AD7689_ADC_DATA_NUMBITS        (16)
#define AD7689_NUM_CHANNELS            (8)
#define AD7689_NUM_INVALID_CONVERSIONS (3)

#define AD7689_INT_REF_MAX_MV                                                  \
    (2500) // chosen using reference buffer selection in config reg
#define AD7689_INT_REF_MIN_MV (0)
#define AD7689_INT_REF_MAX_UV (2500 * 1000)
#define AD7689_INT_REF_MIN_UV (0)
#define AD7689_INT_MAX_STEPS  (0xFFFF + 1)
#define AD7689_TMP_C_FOR_1_MV (25.00 / 283.00)

// Definitions from the fpga
uint32_t AD7689_Reg = 0x0;
uint32_t AD7689_ROReg = 0x0;
uint32_t AD7689_CnvMask = 0x0;
uint32_t AD7689_ClkMask = 0x0;
uint32_t AD7689_DigMask = 0x0;
int AD7689_DigOffset = 0x0;

void AD7689_SetDefines(uint32_t reg, uint32_t roreg, uint32_t cmsk,
                       uint32_t clkmsk, uint32_t dmsk, int dofst) {
    LOG(logDEBUG, ("AD7689: reg:0x%x roreg:0x%x cmsk:0x%x  clkmsk:0x%x  "
                   "dmsk:0x%x  dofst:%d\n",
                   reg, roreg, cmsk, clkmsk, dmsk, dofst));
    AD7689_Reg = reg;
    AD7689_ROReg = roreg;
    AD7689_CnvMask = cmsk;
    AD7689_ClkMask = clkmsk;
    AD7689_DigMask = dmsk;
    AD7689_DigOffset = dofst;
}

void AD7689_Disable() {
    bus_w(AD7689_Reg, (bus_r(AD7689_Reg) & ~(AD7689_CnvMask) & ~AD7689_ClkMask &
                       ~(AD7689_DigMask)));
}

void AD7689_Set(uint32_t codata) {
    LOG(logINFO,
        ("\tSetting ADC SPI Register. Writing 0x%08x to Config Reg\n", codata));
    serializeToSPI(AD7689_Reg, codata, AD7689_CnvMask, AD7689_ADC_CFG_NUMBITS,
                   AD7689_ClkMask, AD7689_DigMask, AD7689_DigOffset, 1);
}

uint16_t AD7689_Get() {
    LOG(logINFO, ("\tGetting ADC SPI Register.\n"));
    return (uint16_t)serializeFromSPI(AD7689_Reg, AD7689_CnvMask,
                                      AD7689_ADC_DATA_NUMBITS, AD7689_ClkMask,
                                      AD7689_DigMask, AD7689_ROReg, 1);
}

int AD7689_GetTemperature() {
    AD7689_Set(
        // read back
        AD7689_CFG_RB_MSK |
        // disable sequencer (different from config)
        AD7689_CFG_SEQ_DSBLE_VAL |
        // Internal reference. REF = 2.5V buffered output. Temperature sensor
        // enabled.
        AD7689_CFG_REF_INT_2500MV_VAL |
        // full bandwidth of low pass filter
        AD7689_CFG_BW_FULL_VAL |
        // all channel (different from config)
        AD7689_CFG_IN_MSK |
        // temperature sensor (different from config)
        AD7689_CFG_INCC_TMP_VAL |
        // overwrite configuration
        AD7689_CFG_CFG_OVRWRTE_VAL);

    int regval = AD7689_Get();

    // value in mV FIXME: page 17? reference voltage temperature coefficient or
    // t do with -40 to 85 °C
    int retval = 0;
    ConvertToDifferentRange(0, AD7689_INT_MAX_STEPS, AD7689_INT_REF_MIN_MV,
                            AD7689_INT_REF_MAX_MV, regval, &retval);
    LOG(logDEBUG1, ("voltage read for temp: %d mV\n", retval));

    // value in °C
    double tempValue = AD7689_TMP_C_FOR_1_MV * (double)retval;
    LOG(logINFO, ("\ttemp read : %f °C (%d unit)\n", tempValue, regval));

    return tempValue;
}

int AD7689_GetChannel(int ichan) {
    // filter channels val
    if (ichan < 0 || ichan >= AD7689_NUM_CHANNELS) {
        LOG(logERROR, ("Cannot get slow adc channel. "
                       "%d out of bounds (0 to %d)\n",
                       ichan, AD7689_NUM_CHANNELS - 1));
        return -1;
    }

    AD7689_Set(
        // read back
        AD7689_CFG_RB_MSK |
        // disable sequencer (different from config)
        AD7689_CFG_SEQ_DSBLE_VAL |
        // Internal reference. REF = 2.5V buffered output. Temperature sensor
        // enabled.
        AD7689_CFG_REF_INT_2500MV_VAL |
        // full bandwidth of low pass filter
        AD7689_CFG_BW_FULL_VAL |
        // specific channel (different from config)
        ((ichan << AD7689_CFG_IN_OFST) & AD7689_CFG_IN_MSK) |
        // input channel configuration (unipolar. inx to gnd)
        AD7689_CFG_INCC_UNPLR_IN_GND_VAL |
        // overwrite configuration
        AD7689_CFG_CFG_OVRWRTE_VAL);

    int regval = AD7689_Get();

    // value in uV
    int retval = ((double)(regval - 0) *
                  (double)(AD7689_INT_REF_MAX_UV - AD7689_INT_REF_MIN_UV)) /
                     (double)(AD7689_INT_MAX_STEPS - 0) +
                 AD7689_INT_REF_MIN_UV;

    /*ConvertToDifferentRange(0, AD7689_INT_MAX_STEPS,
           AD7689_INT_REF_MIN_MV, AD7689_INT_REF_MAX_MV,
           regval, &retval);*/

    LOG(logINFO, ("\tvoltage read for chan %d: %d uV (regVal: %d)\n", ichan,
                  retval, regval));
    return retval;
}

void AD7689_Configure() {
    LOG(logINFOBLUE, ("Configuring AD7689 (Slow ADCs): \n"));

    // from power up, 3 invalid conversions
    LOG(logINFO,
        ("\tConfiguring %d x due to invalid conversions from power up\n",
         AD7689_NUM_INVALID_CONVERSIONS));
    for (int i = 0; i < AD7689_NUM_INVALID_CONVERSIONS; ++i) {
        AD7689_Set(
            // read back
            AD7689_CFG_RB_MSK |
            // scan sequence IN0-IN7 then temperature sensor
            AD7689_CFG_SEQ_SCN_WTH_TMP_VAL |
            // Internal reference. REF = 2.5V buffered output. Temperature
            // sensor enabled.
            AD7689_CFG_REF_INT_2500MV_VAL |
            // full bandwidth of low pass filter
            AD7689_CFG_BW_FULL_VAL |
            // scan upto channel 7
            AD7689_CFG_IN_MSK |
            // input channel configuration (unipolar. inx to gnd)
            AD7689_CFG_INCC_UNPLR_IN_GND_VAL |
            // overwrite configuration
            AD7689_CFG_CFG_OVRWRTE_VAL);
    }
}
