#include <stdint.h>
#include <stdbool.h>  
#include "I2CBUS.h"
#include <xc.h>

#ifdef USE_I2C1
#include "mcc_generated_files/i2c_host/i2c1.h"  
#endif
#ifdef USE_I2C2
#include "mcc_generated_files/i2c_host/i2c2.h"  
#endif
#ifdef USE_I2C3
#include "mcc_generated_files/i2c_host/i2c3.h"  
#endif
#ifdef USE_I2C4
#include "mcc_generated_files/i2c_host/i2c4.h"  
#endif

extern volatile uint16_t tick;
#define I2C_DEVICE_TIMEOUT 50   //MAX=65535 ms, now it is 50ms
#define I2C_MAX_RE_TRY     8    //Max 255
uint8_t I2CWriteRead(I2CBUS_t * pI2CObj);

#ifdef USE_I2C1
static uint8_t ReTry_1;
static uint16_t Delay_1 = 0;
#endif




#ifdef USE_I2C2
static uint8_t ReTry_2;
static uint16_t Delay_2 = 0;
#endif

#ifdef USE_I2C3
static uint8_t ReTry_3;
static uint16_t Delay_3 = 0;
#endif

#ifdef USE_I2C4
static uint8_t ReTry_4;
static uint16_t Delay_4 = 0;
#endif

/**
 * I2C Write-only/ Read-only/ Write-first and then read   Function
 * @param pI2CObj --- I2C Bus object
 * 
 * @return 
 * 0: Success
 * 1: Continue
 * negative: fail
 */
uint8_t I2CWriteRead(I2CBUS_t * pI2CObj) {
    switch (pI2CObj->state) {
        case 0:
            //prepare all I2C1 transaction REQUEST (TRB))
            if (1 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C1      
                if (I2C1_IsBusy() == true) {
                    Delay_1++;
                    if (Delay_1 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                    Delay_1 = 0;
                    return -1; //fail
                }
                //status_1 = I2C1_MESSAGE_PENDING;
                Delay_1 = 0;
                ReTry_1 = 0;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        //I2C1_MasterWriteTRBBuild(&AccessTRB_1[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        //I2C1_MasterReadTRBBuild(&AccessTRB_1[1], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C1_WriteRead(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo] , /*uint8_t *writeData*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t writeLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], /*uint8_t *readData*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t readLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading, so only writing
                        //I2C1_MasterWriteTRBBuild(&AccessTRB_1[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C1_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]);
                    }

                } else {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        //I2C1_MasterReadTRBBuild(&AccessTRB_1[0], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C1_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }

                pI2CObj->state++;
#else
                return -1;
#endif
            } else if (2 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C2
                if (I2C2_IsBusy() == true) {
                    Delay_2++;
                    if (Delay_2 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                    Delay_2 = 0;
                    return -1; //fail
                }
                //status_2 = I2C2_MESSAGE_PENDING;
                Delay_2 = 0;
                ReTry_2 = 0;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        //I2C2_MasterWriteTRBBuild(&AccessTRB_2[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        //I2C2_MasterReadTRBBuild(&AccessTRB_2[1], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C2_WriteRead(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo] , /*uint8_t *writeData*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t writeLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], /*uint8_t *readData*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t readLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading, so only writing
                        //I2C2_MasterWriteTRBBuild(&AccessTRB_2[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C2_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]);
                    }

                } else {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        //I2C2_MasterReadTRBBuild(&AccessTRB_2[0], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C2_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }

                pI2CObj->state++;
#else
                return -1;
#endif
            } else if (3 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C3     
                if (I2C3_IsBusy() == true) {
                    Delay_3++;
                    if (Delay_3 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                    Delay_3 = 0;
                    return -1; //fail
                }
                //status_3 = I2C3_MESSAGE_PENDING;
                Delay_3 = 0;
                ReTry_3 = 0;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        //I2C3_MasterWriteTRBBuild(&AccessTRB_3[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        //I2C3_MasterReadTRBBuild(&AccessTRB_3[1], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C3_WriteRead(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo] , /*uint8_t *writeData*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t writeLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], /*uint8_t *readData*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t readLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading, so only writing
                        //I2C3_MasterWriteTRBBuild(&AccessTRB_3[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C3_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]);
                    }

                } else {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        //I2C3_MasterReadTRBBuild(&AccessTRB_3[0], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C3_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }

                pI2CObj->state++;
#else
                return -1;
#endif
            } else if (4 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C4     
                if (I2C4_IsBusy() == true) {
                    Delay_4++;
                    if (Delay_4 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                    Delay_4 = 0;
                    return -1; //fail
                }
                //status_4 = I2C4_MESSAGE_PENDING;
                Delay_4 = 0;
                ReTry_4 = 0;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        //I2C4_MasterWriteTRBBuild(&AccessTRB_4[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        //I2C4_MasterReadTRBBuild(&AccessTRB_4[1], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C4_WriteRead(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo] , /*uint8_t *writeData*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t writeLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], /*uint8_t *readData*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t readLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading, so only writing
                        //I2C4_MasterWriteTRBBuild(&AccessTRB_4[0], pI2CObj->pWriteData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Write[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C4_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]);
                    }

                } else {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        //I2C4_MasterReadTRBBuild(&AccessTRB_4[0], pI2CObj->pReadData[pI2CObj->PresentSeqNo], pI2CObj->QTY4Read[pI2CObj->PresentSeqNo], pI2CObj->I2CAddr[pI2CObj->PresentSeqNo]);
                        I2C4_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]);
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }

                pI2CObj->state++;
#else                
                return -1;
#endif
            }
            return 1; //continue
            break;
        case 1:
            //Done?
            if (1 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C1      
                if (I2C1_ErrorGet() != I2C_HOST_ERROR_NONE) {
                    Delay_1++;
                    if (Delay_1 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_1 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_1=0;
                        return (-1); //fail
                    } else {
                        ReTry_1++;
                        pI2CObj->state = 0;
                        Delay_1 = 0;
                        return 1; //continue
                    }
                }
                else {
                    pI2CObj->state = 0;
                    Delay_1 = 0;
                    return 0; //success
                }
                return 1; //continue
#else
                pI2CObj->state = 0;
                return -1;
#endif
            } else if (2 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C2      
                if (I2C2_ErrorGet() != I2C_HOST_ERROR_NONE) {
                    Delay_2++;
                    if (Delay_2 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_2 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_2=0;
                        return (-1); //fail
                    } else {
                        ReTry_2++;
                        pI2CObj->state = 0;
                        Delay_2 = 0;
                        return 1; //continue
                    }
                }
                else {
                    pI2CObj->state = 0;
                    Delay_2 = 0;
                    return 0; //success
                }
                return 1; //continue
#else
                pI2CObj->state = 0;
                return -1;
#endif
            } else if (3 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C3 
                if (I2C3_ErrorGet() != I2C_HOST_ERROR_NONE) {
                    Delay_3++;
                    if (Delay_3 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_3 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_3=0;
                        return (-1); //fail
                    } else {
                        ReTry_3++;
                        pI2CObj->state = 0;
                        Delay_3 = 0;
                        return 1; //continue
                    }
                }
                else {
                    pI2CObj->state = 0;
                    Delay_3 = 0;
                    return 0; //success
                }
                return 1; //continue
#else
                pI2CObj->state = 0;
                return -1;
#endif
            } else if (4 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C4 
                if (I2C4_ErrorGet() != I2C_HOST_ERROR_NONE) {
                    Delay_4++;
                    if (Delay_4 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_4 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_4=0;
                        return (-1); //fail
                    } else {
                        ReTry_4++;
                        pI2CObj->state = 0;
                        Delay_4 = 0;
                        return 1; //continue
                    }
                }
                else {
                    pI2CObj->state = 0;
                    Delay_4 = 0;
                    return 0; //success
                }
                return 1; //continue
#else           
                pI2CObj->state = 0;
                return -1;
#endif
            }

    }
    return 1;
}




