#include "tsl25911.h"

#include <Arduino.h>

#define SENSOR_DATA_LENGTH 4

#define ENABLE_POWEROFF (0x00) ///< Flag for ENABLE register to disable
#define ENABLE_POWERON (0x01)  ///< Flag for ENABLE register to enable
#define ENABLE_AEN (0x02) ///< ALS Enable. This field activates ALS function. Writing a one
///< activates the ALS. Writing a zero disables the ALS.
#define ENABLE_AIEN (0x10) ///< ALS Interrupt Enable. When asserted permits ALS interrupts to be
///< generated, subject to the persist filter.
#define ENABLE_NPIEN (0x80) ///< No Persist Interrupt Enable. When asserted NP Threshold conditions
///< will generate an interrupt, bypassing the persist filter

#define LUX_DF (408.0F)   ///< Lux cooefficient
#define LUX_COEFB (1.64F) ///< CH0 coefficient
#define LUX_COEFC (0.59F) ///< CH1 coefficient A
#define LUX_COEFD (0.86F) ///< CH2 coefficient B



/****************************************************************/
bool TSL25911::Initialize() {
    bool success(true);

    success &= ReadChipID();

    if(success) WriteSettings();

    m_initialized = success;
    return m_initialized;
}



/****************************************************************/
bool TSL25911::ReadChipID() {
    uint8_t id;
    ReadRegister(ID_ADDR, &id, 1);
    switch(id) {
    case ChipModel_TSL25911_REV0:
        m_chip_model = ChipModel_TSL25911_REV0;
        break;
    default:
        m_chip_model = ChipModel_UNKNOWN;
        return false;
    }
    return true;
}


/****************************************************************/
void TSL25911::WriteSettings() {
    WriteRegister(REG_ENABLE, ENABLE_POWERON | ENABLE_AEN);
    WriteRegister(REG_CTRL, integration | gain);
    WriteRegister(REG_ENABLE,ENABLE_POWEROFF);
}


/****************************************************************/
bool TSL25911::begin() {
    bool success = Initialize();
    success &= m_initialized;
    return success;
}

/****************************************************************/
bool TSL25911::reset() {
    //Write RST REG ?
    delay(10); //max. startup time according to datasheet
    return(begin());
}

/****************************************************************/
bool TSL25911::ReadData(uint32_t* dataf) {
    uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(dataf));
    bool success = WriteRegister(REG_ENABLE, ENABLE_POWERON | ENABLE_AEN);

    delay((uint32_t)atime()+100);

    success &= ReadRegister(CHAN0_LOW,&data[0],2);
    success &= ReadRegister(CHAN1_LOW,&data[2],2);

    success &= WriteRegister(REG_ENABLE,ENABLE_POWEROFF);

    return success;
}


/****************************************************************/


float ComputeLux(uint16_t ch0, uint16_t ch1, float atime, float again) {
    float cpl, lux1, lux2, lux;

    if ((ch0 == 0xFFFF) | (ch1 == 0xFFFF)) return -1;

    cpl = (atime * again) / LUX_DF;
    lux = (((float)ch0 - (float)ch1)) * (1.0F - ((float)ch1 / (float)ch0)) / cpl;
    return lux;
}

uint16_t TSL25911::lumV() {

    if(!m_initialized) return 0;
    uint32_t data;
    if(ReadData(&data))
        return ((data & 0xFFFF) - (data >> 16));
    return 0;
}

uint16_t TSL25911::lumIR() {
    if(!m_initialized) return 0;
    uint32_t data;
    if(ReadData(&data))
        return (data >> 16);
    return 0;
}

uint16_t TSL25911::lumF() {
    if(!m_initialized) return 0;
    uint32_t data;
    if(ReadData(&data))
        return (data & 0xFFFF);
    return 0;
}

float TSL25911::lumLux() {
    if(!m_initialized) return 0;
    uint32_t data;
    if(ReadData(&data)) {
        return ComputeLux(data&0xFFFF, data>>16,again(),atime());
    }
    return 0;
}

void TSL25911::read(uint16_t& v, uint16_t& ir, uint16_t& f, float& lux) {
    if(!m_initialized) return;
    uint32_t data;
    if(!ReadData(&data)) return;
    v = ((data & 0xFFFF) - (data >> 16));
    ir = (data >> 16);
    f = (data & 0xFFFF);
    lux = ComputeLux(data&0xFFFF,data>>16,again(),atime());
}


/****************************************************************/
TSL25911::ChipModel TSL25911::chipModel() {
    return m_chip_model;
}


/****************************************************************/
bool TSL25911::WriteRegister(uint8_t addr, uint8_t data) {
    i2c->beginTransmission(TSL25911_ADDRESS);
    i2c->write(addr);
    i2c->write(data);
    i2c->endTransmission();
    return true;
}


/****************************************************************/
bool TSL25911::ReadRegister(uint8_t addr,uint8_t data[],uint8_t length) {
    uint8_t ord(0);

    i2c->beginTransmission(TSL25911_ADDRESS);
    i2c->write(addr);
    i2c->endTransmission();

    i2c->requestFrom(static_cast<uint8_t>(TSL25911_ADDRESS), length);

    while(i2c->available()) {
        data[ord++] = i2c->read();
    }

    return ord == length;
}

float TSL25911::again() {
    switch (gain) {
    case TSL2591_GAIN_LOW:
        return 1.0F;
        break;
    case TSL2591_GAIN_MED:
        return 25.0F;
        break;
    case TSL2591_GAIN_HIGH:
        return 428.0F;
        break;
    case TSL2591_GAIN_MAX:
        return 9876.0F;
        break;
    default:
        return 1.0F;
        break;
    }
}

float TSL25911::atime() {
    switch (integration) {
    case TSL2591_INTEGRATIONTIME_100MS:
        return 100.0F;
        break;
    case TSL2591_INTEGRATIONTIME_200MS:
        return 200.0F;
        break;
    case TSL2591_INTEGRATIONTIME_300MS:
        return 300.0F;
        break;
    case TSL2591_INTEGRATIONTIME_400MS:
        return 400.0F;
        break;
    case TSL2591_INTEGRATIONTIME_500MS:
        return 500.0F;
        break;
    case TSL2591_INTEGRATIONTIME_600MS:
        return 600.0F;
        break;
    default: // 100ms
        return 100.0F;
        break;
    }
}