///
/// @file sht3x.c
/// @authors k. Headley
/// @date 05 dec 2019

/// Sensirion SHT3X I2C RH/Temp sensor driver

/// @sa doxygen-examples.c for more examples of Doxygen markup


/////////////////////////
// Terms of use
/////////////////////////
/*
 Copyright Information

 Copyright 2002-2019 MBARI
 Monterey Bay Aquarium Research Institute, all rights reserved.

 Terms of Use

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version. You can access the GPLv3 license at
 http://www.gnu.org/licenses/gpl-3.0.html

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details
 (http://www.gnu.org/licenses/gpl-3.0.html)

 MBARI provides the documentation and software code "as is", with no warranty,
 express or implied, as to the software, title, non-infringement of third party
 rights, merchantability, or fitness for any particular purpose, the accuracy of
 the code, or the performance or results which you may obtain from its use. You
 assume the entire risk associated with use of the code, and you agree to be
 responsible for the entire cost of repair or servicing of the program with
 which you are using the code.

 In no event shall MBARI be liable for any damages, whether general, special,
 incidental or consequential damages, arising out of your use of the software,
 including, but not limited to, the loss or corruption of your data or damages
 of any kind resulting from use of the software, any prohibited use, or your
 inability to use the software. You agree to defend, indemnify and hold harmless
 MBARI and its officers, directors, and employees against any claim, loss,
 liability or expense, including attorneys' fees, resulting from loss of or
 damage to property or the injury to or death of any person arising out of the
 use of the software.

 The MBARI software is provided without obligation on the part of the
 Monterey Bay Aquarium Research Institute to assist in its use, correction,
 modification, or enhancement.

 MBARI assumes no responsibility or liability for any third party and/or
 commercial software required for the database or applications. Licensee agrees
 to obtain and maintain valid licenses for any additional third party software
 required.
 */

/////////////////////////
// Headers
/////////////////////////
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "sht3x.h"

/////////////////////////
// Macros
/////////////////////////

// These macros should only be defined for
// application main files rather than general C files
/*
/// @def PRODUCT
/// @brief header software product name
#define PRODUCT "carbonwg"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-2019 MBARI Monterey Bay Aquarium Research Institute, all rights reserved."
/// @def NOWARRANTY
/// @brief header software terms of use
#define NOWARRANTY  \
"This program is distributed in the hope that it will be useful,\n"\
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"\
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"\
"GNU General Public License for more details (http://www.gnu.org/licenses/gpl-3.0.html)\n"
*/

#define SHT3X_HRESET_MASK_A  (1 << SHT3X_HRESET_BIT_A)
#define SHT3X_HRESET_MASK_B  (1 << SHT3X_HRESET_BIT_B)
#define SHT3X_SET_HRESET_A()  SHT3X_HRESET_PORT_A |= SHT3X_HRESET_MASK_A
#define SHT3X_CLR_HRESET_A()  SHT3X_HRESET_PORT_A &= ~SHT3X_HRESET_MASK_A
#define SHT3X_SET_HRESET_B()  SHT3X_HRESET_PORT_B |= SHT3X_HRESET_MASK_B
#define SHT3X_CLR_HRESET_B()  SHT3X_HRESET_PORT_B &= ~SHT3X_HRESET_MASK_B

/////////////////////////
// Declarations
/////////////////////////

/////////////////////////
// Imports
/////////////////////////

/////////////////////////
// Module Global Variables
/////////////////////////
uint16_t SHT3X_PORT_INVALID = 0xFFFF;

sht3x_t sht3x_table[]={
    {SHT3X_I2C_HND,SHT3X_PRI_MODE_DFL,{0,0,{0,0}}}
};
/////////////////////////
// Function Definitions
/////////////////////////
static sht3x_t *s_hnd_instance(sht3x_hnd_t hnd)
{
    sht3x_t *instance=NULL;
    if(SHT3X_HRANGE_VALID(hnd)){
        instance = &sht3x_table[hnd];
    }
    return instance;
}

static int s_ss_cmd(sht3x_t *self, i2c_addr_t *addr, uint16_t *cmd)
{
    int retval=0;

    if(NULL!=self){
            if(NULL!=addr){
                *addr = (byte) (SHT3X_MODE_ADDR_A(self) ? CMD_ADDR_A : CMD_ADDR_B);
            }

            switch(SHT3X_MODE_RPT(self)){
                case MODE_RPT_L:
                    if(SHT3X_MODE_CS_EN(self)){
                        if(NULL!=cmd){
                            *cmd = CMD_SS_CSRL;
                        }
                    }else{
                        if(NULL!=cmd){
                            *cmd = CMD_SS_NCSRL;
                        }
                    }
                break;
                case MODE_RPT_M:
                    if(SHT3X_MODE_CS_EN(self)){
                        if(NULL!=cmd){
                            *cmd = CMD_SS_CSRM;
                        }
                    }else{
                        if(NULL!=cmd){
                            *cmd = CMD_SS_NCSRM;
                        }
                    }
                break;
                case MODE_RPT_H:
                    if(SHT3X_MODE_CS_EN(self)){
                        if(NULL!=cmd){
                            *cmd = CMD_SS_CSRH;
                        }
                    }else{
                        if(NULL!=cmd){
                            *cmd = CMD_SS_NCSRH;
                        }
                    }
                break;
                default:
                    retval=-1;
                break;
            }
    }else{
        retval=-1;
    }
    return retval;
}

static int s_sht3x_send_cmd(sht3x_t *self, uint16_t cmd)
{
    int retval=-1;
    if(NULL!=self){
        uint32_t bw=0;
        if(i2cp_send_bytes(self->bus,SHT3X_ADDR(self), (byte *)&cmd, 2, &bw)){
            retval = 0;
        }
    }
    return retval;
}

void sht3x_init(sht3x_hnd_t hnd)
{
    return;
}

byte sht3x_crc8(byte *data, uint32_t len)
{
    uint8_t crc = SHT3X_CRC_INIT;
    uint32_t i, j;
    for (i = 0; i < len; i++) {
        crc ^= data[i];
        for (j = 0; j < 8; j++) {
            if ((crc & 0x80) != 0){
                crc = (byte)((crc << 1) ^ SHT3X_CRC_POLY);
            }else{
                crc <<= 1;
            }
        }
    }
    return crc;
}

int sht3x_power(sht3x_hnd_t hnd, bool state)
{
    int retval=-1;

    return retval;
}

int sht3x_hreset(sht3x_hnd_t hnd)
{
    int retval=0;
    // reset using hard reset (VDD powered on digital IO) or
    // nreset pin, if implemented in hardware.
    if((SHT3X_HRESET_PORT_A != SHT3X_PORT_INVALID) &&
       SHT3X_BIT_VALID(SHT3X_HRESET_BIT_A)){
        SHT3X_CLR_HRESET_A();
        SHT3X_HRESET_DELAY_MSEC();
        SHT3X_SET_HRESET_A();
    }

    if((SHT3X_HRESET_PORT_B != SHT3X_PORT_INVALID) &&
       SHT3X_BIT_VALID(SHT3X_HRESET_BIT_B)){
        SHT3X_CLR_HRESET_B();
        SHT3X_HRESET_DELAY_MSEC();
        SHT3X_SET_HRESET_B();
    }

    return retval;
}


int sht3x_heater(sht3x_hnd_t hnd, bool state)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        retval=s_sht3x_send_cmd(self, (uint16_t)(state ? CMD_HEATER_EN : CMD_HEATER_DI) );
    }
    return retval;
}

int sht3x_art_en(sht3x_hnd_t hnd)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        retval=s_sht3x_send_cmd(self, (uint16_t)CMD_ART );
    }
    return retval;
}

int sht3x_break(sht3x_hnd_t hnd)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        retval=s_sht3x_send_cmd(self, (uint16_t)CMD_BREAK );
    }
    return retval;
}

int sht3x_per_start(sht3x_hnd_t hnd)
{
    int retval=-1;
    return retval;
}

int sht3x_per_read(sht3x_hnd_t hnd)
{
    int retval=-1;
    return retval;
}

int sht3x_clr_sreg(sht3x_hnd_t hnd)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        retval=s_sht3x_send_cmd(self, (uint16_t)CMD_CLR_SREG );
    }
    return retval;
}

int sht3x_sreset(sht3x_hnd_t hnd)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){

        i2c_addr_t addr = (byte) (SHT3X_MODE_ADDR_A(self) ? CMD_ADDR_A : CMD_ADDR_B);
        uint32_t bw=0;
        uint16_t cmd = CMD_SRST;
        if(i2cp_send_bytes(self->bus, addr, (byte *)&cmd, 2, &bw)){
            retval = 0;
        }
    }
    return retval;
}

int sht3x_gcall(sht3x_hnd_t hnd)
{
    int retval=-1;

    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        i2c_addr_t addr = ADDR_GEN_CALL;
        byte cmd = SHT3X_LSB(CMD_GRST);
        uint32_t bw=0;
        if(i2cp_send_bytes(self->bus, addr, (byte *)&cmd, 1, &bw)){
            retval = 0;
        }
    }
    return retval;
}

int sht3x_ss_read(sht3x_hnd_t hnd, int32_t retries)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        i2c_addr_t addr = 0x00;
        uint16_t cmd = 0x00;

        if( s_ss_cmd(self, &addr, &cmd)==0){
            uint32_t bw=0;

            if( i2cp_send_bytes(self->bus, addr, (byte *)&cmd, 2, &bw) && bw==2){

//                // wait for data
//                if(SHT3X_MODE_CS_EN(self)){
//                    // clock-stretch enabled:
//                    // block until scl released
//
//                    while(i2cp_test_scl(self->bus)==false ){
//                        I2CX_DELAY();
//                        if(retries>0){
//                            if(--retries <= 0)
//                                break;
//                        }
//                    }
//                }else{
//                    // clock-stretch disabled:
//                    // block until read header responds w/ ACK
//
//                    bool test = i2cp_wrbyte(self->bus, addr, true, false);
//
//                    while( (test==false || i2cp_flag_isset(self->bus, I2CX_EWRITE)) ){
//                        I2CX_DELAY();
//                        if(retries>0){
//                            if(--retries <= 0)
//                                break;
//                        }
//                        test = i2cp_wrbyte(self->bus, addr, true, false);
//                    }
//                }
                uint32_t rlen=sizeof(sht3x_ssmeas_t);
                uint32_t br=0;
                byte meas[rlen];
                memset(meas,0,sizeof(sht3x_ssmeas_t));
                if(i2cp_recv_bytes(self->bus,addr, meas, rlen, &br) && br>=rlen){
                    // temp MSB, LSB
                    memcpy((void *)self->meas.temp,&meas[0],sizeof(uint16_t));
                    // RH MSB. LSB
                    memcpy((void *)self->meas.rh,&meas[3],sizeof(uint16_t));
                    // temp CRC byte
                    self->meas.crc[0]=meas[2];
                    // RH CRC byte
                    self->meas.crc[1]=meas[5];
                }
            }
        }// cmd failed
    }// invalid arg
    return retval;
}

int sht3x_sreg(sht3x_hnd_t hnd, byte *dest, size_t len)
{
    int retval=-1;
    sht3x_t *self = s_hnd_instance(hnd);
    if(NULL!=self){
        // send read sreg
        if(s_sht3x_send_cmd(self,CMD_RD_SREG)==0){
            byte sreg[SHT3X_SREG_BYTES]={0};
            uint32_t br=0;
            // read sreg bytes
            if(i2cp_recv_bytes(self->bus, SHT3X_ADDR(self), sreg, SHT3X_SREG_BYTES, &br) && br==3){
                // validate and (optionally) populate dest
                if(sreg[2]==sht3x_crc8(sreg, 2) ){
                    retval=0;
                    if(NULL!=dest){
                        memcpy(dest,sreg,( SHT3X_SREG_BYTES>len ? len : SHT3X_SREG_BYTES ));
                    }
                }// else invalid
            }// else read error
        }// else write error
    }// else invalid arg
    return retval;
}

