/******************************************************************************
*
* Revision: A
* Author: MAW
* Created:  17 Jan 2018
*
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*  FILE: IMX290.h
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* DESCRIPTION: Header file to define functions for the IMX290 CMOS imager.
*
*
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*	Copyright (c) 2018, Metrohm Raman
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/

#include <stddef.h>
#include <math.h>
#include <stdio.h>
#include <strings.h>

//SDI UART
#include "sdi_task.h"
#include "sdi_tl_uart.h"

/* Board Header files */
#include "Board.h"
#include "Parse.h"
#include "SRI_Global.h"
#include "EEPROM.h"
#include "PCA9536.h"
#include "Parse.h"
#include "IMX290.h"
#include "i2c.h"
#include "PCA9536.h"
#include "IMX290_spi.h"

uint8_t multiBuffer[5];

/* Note: I2C transfers are MSB first. */
/* Functions */
void IMX290_Power(bool control)
{
   if (control == ON)
   {
       Control_1v2_En(ON);
       usleep(10*1000);
       Control_1v8_En(ON);
       usleep(10*1000);
       Control_2v9_En(ON);
       /* delay for oscillator stabilization. */
       usleep(10*1000);
   }
   else
   {
       Control_2v9_En(OFF);
       usleep(1*1000);
       Control_1v8_En(OFF);
       usleep(1*1000);
       Control_1v2_En(OFF);
       usleep(1*1000);
       GPIO_clearDio(MINIRAMAN_nFPGA_RST);
   }
}


bool IMX290_Init(void)
{
    IMX290SpiOpen();
    GPIO_setDio(MINIRAMAN_IMAGER_XCLR);
    return(true);
}
#define SLOW_CLOCK

bool IMX290_Start(void)
{
    uint16_t WINPV;     // Top edge of cropping window

    IMX290_Power(ON);
    IMX_290_Reset();
    IMX290_Write(IMX290_STANDBY, IMX290_STANDBY_STANDBY);
    IMX290_Write(IMX290_XMSTA, IMX290_XMSTA_STOP);
    IMX290_Write(IMX290_REGHOLD, IMX290_REGHOLD_ON);

    IMX290_Write(IMX290_ADBIT, IMX290_ADBIT_INIT);
    IMX290_Write(IMX290_IMAGE_MODES, IMX290_IMAGE_MODES_CROPMODE);
//    IMX290_Write(IMX290_IMAGE_MODES, IMX290_IMAGE_MODES_720MODE);  // 720p
    IMX290_Write(IMX290_WINWV_LOW, 0x30);
    IMX290_Write(IMX290_WINWV_HIGH, 0x01);
    // Set window position
    WINPV = eeprom_base.eeprom.ImagerCenterline - (IMX290_MIN_WINWV/2);
    IMX290_Write(IMX290_WINPV_LOW, (WINPV & 0x00FF));
    IMX290_Write(IMX290_WINPV_HIGH, ((WINPV & 0xFF00) >> 8));
    IMX290_Write(IMX290_WINWH_LOW, 0x90);
    IMX290_Write(IMX290_WINWH_HIGH, 0x07);
    IMX290_Write(IMX290_WINWV_OB, IMX290_WINWV_OB_INIT);
    IMX290_Write(IMX290_XHSLNG, IMX290_XVSLNG_0);
    // Set Gain range based on eeprom value.
    if (eeprom_base.eeprom.ImagerGainSelect == 0) IMX290_Write(IMX290_RATE_GAIN, IMX290_RATE_GAIN_INIT_LOW);
    else IMX290_Write(IMX290_RATE_GAIN, IMX290_RATE_GAIN_INIT_HIGH);
    IMX290_Write(IMX290_0x300F, IMX290_0x300F_INIT);
    IMX290_Write(IMX290_0x3010, IMX290_0x3010_INIT);
    IMX290_Write(IMX290_0x3012, IMX290_0x3012_INIT);
    IMX290_Write(IMX290_0x3013, IMX290_0x3013_INIT);
    // Set Gain
    IMX290_Write(IMX290_GAIN, eeprom_base.eeprom.ImagerGain);
    IMX290_Write(IMX290_0x3016, IMX290_0x3016_INIT);

#ifdef SLOW_CLOCK
    IMX290_Write(IMX290_INCKSEL1, 0x10);
    IMX290_Write(IMX290_INCKSEL2, 0x00);
    IMX290_Write(IMX290_INCKSEL3, 0x10);
    IMX290_Write(IMX290_INCKSEL4, 0x01);
//    IMX290_Write(IMX290_EXTCLK_FREQ_HIGH, (uint8_t)(IMX290_EXTCLK_FREQ_74_250M >> 8));
//    IMX290_Write(IMX290_EXTCLK_FREQ_LOW, (uint8_t)(IMX290_EXTCLK_FREQ_74_250M & 0xFF));
    IMX290_Write(IMX290_EXTCLK_FREQ_HIGH, (uint8_t)(IMX290_EXTCLK_FREQ_37_125M >> 8));
    IMX290_Write(IMX290_EXTCLK_FREQ_LOW, (uint8_t)(IMX290_EXTCLK_FREQ_37_125M & 0xFF));
    IMX290_Write(IMX290_INCKSEL5, 0x1B);
    IMX290_Write(IMX290_INCKSEL6, 0x1B);
    IMX290_Write(IMX290_INCKSEL7, 0x92);
    IMX290_Write(IMX290_HMAX_LOW, 0x30);
    IMX290_Write(IMX290_HMAX_HIGH, 0x11);
#else
    IMX290_Write(IMX290_INCKSEL1, IMX290_INCKSEL1_INIT);
    IMX290_Write(IMX290_INCKSEL2, IMX290_INCKSEL2_INIT);
    IMX290_Write(IMX290_INCKSEL3, IMX290_INCKSEL3_INIT);
    IMX290_Write(IMX290_INCKSEL4, IMX290_INCKSEL4_INIT);
    IMX290_Write(IMX290_INCKSEL5, IMX290_INCKSEL5_INIT);
    IMX290_Write(IMX290_INCKSEL6,  IMX290_INCKSEL6_INIT);
    IMX290_Write(IMX290_INCKSEL7, IMX290_INCKSEL7_INIT);
    IMX290_Write(IMX290_EXTCLK_FREQ_HIGH, (uint8_t)(IMX290_EXTCLK_FREQ_37_125M >> 8));
    IMX290_Write(IMX290_EXTCLK_FREQ_LOW, (uint8_t)(IMX290_EXTCLK_FREQ_37_125M & 0xFF));
#endif

    IMX290_Write(IMX290_SHS1_LOW, 0x01);
    IMX290_Write(IMX290_SHS1_MID, 0x00);
    IMX290_Write(IMX290_SHS1_HIGH, 0x00);
    IMX290_Write(IMX290_RESOLUTION_PORT_SEL, IMX290_RESOLUTION_PORT_SEL_INIT);
    IMX290_Write(IMX290_BLK_LEVEL, IMX290_BLK_LEVEL_INIT);
    IMX290_Write(IMX290_0x3070, IMX290_0x3070_INIT);
    IMX290_Write(IMX290_0x3071, IMX290_0x3071_INIT);
    IMX290_Write(IMX290_0x309B, IMX290_0x309B_INIT);
    IMX290_Write(IMX290_0x309C, IMX290_0x309C_INIT);
    IMX290_Write(IMX290_0x30A2, IMX290_0x30A2_INIT);
    IMX290_Write(IMX290_0x30A6, IMX290_0x30A6_INIT);
    IMX290_Write(IMX290_0x30A8, IMX290_0x30A8_INIT);
    IMX290_Write(IMX290_0x30AA, IMX290_0x30AA_INIT);
    IMX290_Write(IMX290_0x30AC, IMX290_0x30AC_INIT);
    IMX290_Write(IMX290_0x30B0, IMX290_0x30B0_INIT);
    IMX290_Write(IMX290_0x3119, IMX290_0x3119_INIT);
    IMX290_Write(IMX290_0x311C, IMX290_0x311C_INIT);
    IMX290_Write(IMX290_0x311E, IMX290_0x311E_INIT);
    IMX290_Write(IMX290_0x3128, IMX290_0x3128_INIT);
    IMX290_Write(IMX290_ADBIT1, IMX290_ADBIT1_INIT_12B);
    IMX290_Write(IMX290_0x313D, IMX290_0x313D_INIT);
    IMX290_Write(IMX290_0x3150, IMX290_0x3150_INIT);

    IMX290_Write(IMX290_ADBIT2, IMX290_ADBIT2_INIT_12B);
    IMX290_Write(IMX290_0x317E, IMX290_0x317E_INIT);
    IMX290_Write(IMX290_ADBIT3, IMX290_ADBIT3_INIT_12B);
    IMX290_Write(IMX290_0x32B8, IMX290_0x32B8_INIT);
    IMX290_Write(IMX290_0x32B9, IMX290_0x32B9_INIT);
    IMX290_Write(IMX290_0x32BA, IMX290_0x32BA_INIT);
    IMX290_Write(IMX290_0x32BB, IMX290_0x32BB_INIT);
    IMX290_Write(IMX290_0x32C8, IMX290_0x32C8_INIT);
    IMX290_Write(IMX290_0x32C9, IMX290_0x32C9_INIT);
    IMX290_Write(IMX290_0x32CA, IMX290_0x32CA_INIT);
    IMX290_Write(IMX290_0x32CB, IMX290_0x32CB_INIT);
    IMX290_Write(IMX290_0x332C, IMX290_0x332C_INIT);
    IMX290_Write(IMX290_0x332D, IMX290_0x332D_INIT);
    IMX290_Write(IMX290_0x332E, IMX290_0x332E_INIT);
    IMX290_Write(IMX290_0x3358, IMX290_0x3358_INIT);
    IMX290_Write(IMX290_0x3359, IMX290_0x3359_INIT);
    IMX290_Write(IMX290_0x335A, IMX290_0x335A_INIT);
    IMX290_Write(IMX290_0x3360, IMX290_0x3360_INIT);
    IMX290_Write(IMX290_0x3361, IMX290_0x3361_INIT);
    IMX290_Write(IMX290_0x3362, IMX290_0x3362_INIT);
    IMX290_Write(IMX290_0x33B0, IMX290_0x33B0_INIT);
    IMX290_Write(IMX290_0x33B2, IMX290_0x33B2_INIT);
    IMX290_Write(IMX290_0x33B3, IMX290_0x33B3_INIT);

    // Set for minimum integration time of 10 mS
    IMX290_Write(IMX290_VMAX_LOW, 0x4D);
    IMX290_Write(IMX290_VMAX_MID, 0x01); // CROP MODE
    IMX290_Write(IMX290_VMAX_HIGH, 0x00);
//      IMX290_Write(IMX290_VMAX_LOW, 0xEE);
//      IMX290_Write(IMX290_VMAX_MID, 0x02); // 720P
//      IMX290_Write(IMX290_VMAX_HIGH, 0x00);

    /* Pattern Generator */
//    IMX290_Write(IMX290_BLK_LEVEL, 0x00);
//    IMX290_Write(0x300E, 0x00);
//    IMX290_Write(0x300F, 0x00);
//    IMX290_Write(0x308C, 0x51); // Upper nibble determines pattern
    /* End Pattern Generator */

    IMX290_Write(IMX290_REGHOLD, IMX290_REGHOLD_OFF);
    IMX290_Write(IMX290_STANDBY, IMX290_STANDBY_RUN);
    usleep(20*1000);  // Regulator Stabilization in IMX290
    IMX290_Write(IMX290_XMSTA, IMX290_XMSTA_START);
    return (true);
}


bool IMX290_Write(uint16_t reg_addr, uint8_t value)
{
    bool i2c_return;

    i2c_txBuffer[0] = (reg_addr >>8);
    i2c_txBuffer[1] = (reg_addr & 0x00FF);
    i2c_txBuffer[2] = value;

    i2cTransaction.slaveAddress = IMX290_ADDR;
    i2cTransaction.writeBuf = i2c_txBuffer;
    i2cTransaction.writeCount = 3;
    i2cTransaction.readBuf = i2c_rxBuffer;
    i2cTransaction.readCount = 0;
    i2c_return = I2C_transfer(i2c, &i2cTransaction);
    return(i2c_return);
}


uint8_t IMX290_Read(uint16_t reg_addr)
{
    bool i2c_return;

    i2c_txBuffer[0] = (reg_addr >>8);
    i2c_txBuffer[1] = (reg_addr & 0x00FF);

    i2cTransaction.slaveAddress = IMX290_ADDR;
    i2cTransaction.writeBuf = i2c_txBuffer;
    i2cTransaction.writeCount = 2;
    i2cTransaction.readBuf = i2c_rxBuffer;
    i2cTransaction.readCount = 1;
    i2c_return = I2C_transfer(i2c, &i2cTransaction);
    if (i2c_return == false) return (0);
    return(i2c_rxBuffer[0]);
}


bool IMX290_MultiWrite(uint16_t reg_addr, uint8_t *values, uint8_t numBytes)
{
    uint8_t byte_count;
    bool i2c_return;

    IMX290_Write(IMX290_REGHOLD, IMX290_REGHOLD_ON);
    /* 2 bytes of xmit buffer are used for the register address. */
    if (numBytes > (I2C_BUFF_SIZE-2)) return(false);
    i2c_txBuffer[0] = (uint8_t)(reg_addr >> 8);
    i2c_txBuffer[1] = (uint8_t)(reg_addr & 0x00FF);
    for (byte_count = 0; byte_count < numBytes; byte_count++)
    {
        i2c_txBuffer[byte_count+2] = values[byte_count];
    }
    i2cTransaction.slaveAddress = IMX290_ADDR;
    i2cTransaction.writeBuf = i2c_txBuffer;
    i2cTransaction.writeCount = (2 + numBytes);
    i2cTransaction.readBuf = i2c_rxBuffer;
    i2cTransaction.readCount = 0;
    i2c_return = I2C_transfer(i2c, &i2cTransaction);
    IMX290_Write(IMX290_REGHOLD, IMX290_REGHOLD_OFF);
    return(i2c_return);
}


bool IMX290_MultiRead(uint16_t reg_addr, uint8_t *values, uint8_t numBytes)
{
    uint8_t byte_count;
    bool i2c_return;

    if (numBytes > I2C_BUFF_SIZE) return(false);
    i2c_txBuffer[0] = (uint8_t)(reg_addr >> 8);
    i2c_txBuffer[1] = (uint8_t)(reg_addr & 0x00FF);

    i2cTransaction.slaveAddress = IMX290_ADDR;
    i2cTransaction.writeBuf = i2c_txBuffer;
    i2cTransaction.writeCount = 2;
    i2cTransaction.readBuf = i2c_rxBuffer;
    i2cTransaction.readCount = numBytes;
    i2c_return = I2C_transfer(i2c, &i2cTransaction);
    for (byte_count = 0; byte_count < numBytes; byte_count++)
    {
        values[byte_count] = i2c_rxBuffer[byte_count];
    }
    return(i2c_return);
}


void IMX_290_Reset(void)
{
    IMX290_Write(IMX290_SW_RESET, IMX290_SW_RESET_ACTIVE);
    usleep(20*1000);
}

/* Start command */
void IMX290_AcquireSpectrum(float IntTime)
{
    uint8_t dummyFrameCount = 0;
    float int_time_ticks;
    uint32_t int_time_hex;
    uint8_t value_buff[4];

    IMX290_Start();

    GPIO_clearDio(MINIRAMAN_nFPGA_RST);
    usleep(1*1000);
    GPIO_setDio(MINIRAMAN_nFPGA_RST);
    IMX290SpiFlush();
    while (dummyFrameCount <= 7)
    {
        while (!GPIO_readDio(MINIRAMAN_FPGA_DONE));
        ++dummyFrameCount;
        if (dummyFrameCount == 6)
        {
            // Set Integration time
            IMX290_Write(IMX290_REGHOLD, IMX290_REGHOLD_ON);
            int_time_ticks = status.integration_time/IMX290_1H_TIME;
            int_time_hex = (uint32_t)int_time_ticks;
            value_buff[0] = (uint8_t)(int_time_hex & 0xFF);
            value_buff[1] = (uint8_t)((int_time_hex & 0x0000FF00) >> 8);
            value_buff[2] = (uint8_t)((int_time_hex & 0x000F0000) >> 16);
            IMX290_Write(IMX290_VMAX_LOW, value_buff[0]);
            IMX290_Write(IMX290_VMAX_MID, value_buff[1]);
            IMX290_Write(IMX290_VMAX_HIGH, value_buff[2]);
            IMX290_Write(IMX290_REGHOLD, IMX290_REGHOLD_OFF);
        }
        while (GPIO_readDio(MINIRAMAN_FPGA_DONE));
    }
    while (!GPIO_readDio(MINIRAMAN_FPGA_DONE));
    /* Note, Pixel clock must remain active during ReadSpectrum(). */
    IMX290SpiRead(spectrum, SPECTRUM_SIZE);
    IMX290_Power(OFF);
}


bool IMX290_SetWindow(uint16_t xStart, uint16_t xSize, uint16_t yStart, uint16_t ySize)
{
    return (true);
}

extern void IMX290_RegWrite(uint16_t reg_addr, uint8_t LSB_Position,  uint8_t Field_Width, uint32_t New_Value)
{

}

/******************************************************************************
* Done.
*/
