#include <stdint.h>
#include <stdbool.h>  
#include "I2CBUS.h"
#include <xc.h>
#include "config/default/definitions.h"

typedef enum {
    APP_TRANSFER_STATUS_IN_PROGRESS,
    APP_TRANSFER_STATUS_SUCCESS,
    APP_TRANSFER_STATUS_ERROR,
    APP_TRANSFER_STATUS_IDLE,
} APP_TRANSFER_STATUS;

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
extern volatile APP_TRANSFER_STATUS transferStatus1;
static uint8_t ReTry_1;
static uint16_t Delay_1 = 0;
#endif

#ifdef USE_I2C2
extern volatile APP_TRANSFER_STATUS transferStatus2;
static uint8_t ReTry_2;
static uint16_t Delay_2 = 0;
#endif

#ifdef USE_I2C3
extern volatile APP_TRANSFER_STATUS transferStatus3;
static uint8_t ReTry_3;
static uint16_t Delay_3 = 0;
#endif

#ifdef USE_I2C4
extern volatile APP_TRANSFER_STATUS transferStatus4;
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   
                transferStatus1 = APP_TRANSFER_STATUS_IN_PROGRESS;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        if (SERCOM0_I2C_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]) == false) {
                            //fail to request
                            Delay_1++;
                            if (Delay_1 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_1 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                        
                       
                    } else {
                        //no reading, so only writing
                        if (SERCOM0_I2C_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_1++;
                            if (Delay_1 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_1 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                            
                       
                    }
                }
                else
                {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        if (SERCOM0_I2C_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t* rdata*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t rlength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_1++;
                            if (Delay_1 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_1 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }                        
                        
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }                    

#else
                return -1;
#endif
            } else if (2 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C2
                transferStatus2 = APP_TRANSFER_STATUS_IN_PROGRESS;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        if (SERCOM1_I2C_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]) == false) {
                            //fail to request
                            Delay_2++;
                            if (Delay_2 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_2 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                        
                       
                    } else {
                        //no reading, so only writing
                        if (SERCOM1_I2C_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_2++;
                            if (Delay_2 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_2 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                            
                       
                    }
                }
                else
                {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        if (SERCOM1_I2C_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t* rdata*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t rlength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_2++;
                            if (Delay_2 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_2 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }                        
                        
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }                    

#else
                return -1;
#endif
            } else if (3 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C3     
                transferStatus3 = APP_TRANSFER_STATUS_IN_PROGRESS;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        if (SERCOM2_I2C_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]) == false) {
                            //fail to request
                            Delay_3++;
                            if (Delay_3 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_3 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                        
                       
                    } else {
                        //no reading, so only writing
                        if (SERCOM2_I2C_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_3++;
                            if (Delay_3 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_3 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                            
                       
                    }
                }
                else
                {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        if (SERCOM2_I2C_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t* rdata*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t rlength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_3++;
                            if (Delay_3 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_3 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }                        
                        
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }                                 
                
#else
                return -1;
#endif
            } else if (4 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C4                  
                transferStatus4 = APP_TRANSFER_STATUS_IN_PROGRESS;
                if (pI2CObj->QTY4Write[pI2CObj->PresentSeqNo] > 0) {
                    //writing exists
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //both writing and reading
                        if (SERCOM3_I2C_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]) == false) {
                            //fail to request
                            Delay_4++;
                            if (Delay_4 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_4 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                        
                       
                    } else {
                        //no reading, so only writing
                        if (SERCOM3_I2C_Write(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t *data*/pI2CObj->pWriteData[pI2CObj->PresentSeqNo], /*size_t dataLength*/pI2CObj->QTY4Write[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_4++;
                            if (Delay_4 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_4 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }
                            
                       
                    }
                }
                else
                {
                    //no writing
                    if (pI2CObj->QTY4Read[pI2CObj->PresentSeqNo] > 0) {
                        //only reading
                        if (SERCOM3_I2C_Read(/*uint16_t address*/pI2CObj->I2CAddr[pI2CObj->PresentSeqNo], /*uint8_t* rdata*/pI2CObj->pReadData[pI2CObj->PresentSeqNo], /*size_t rlength*/pI2CObj->QTY4Read[pI2CObj->PresentSeqNo]) == false) {
                            //fail to request
                            Delay_4++;
                            if (Delay_4 < I2C_DEVICE_TIMEOUT * I2C_MAX_RE_TRY) return 1; //continue
                            Delay_4 = 0;
                            return -1; //fail
                        }
                        else
                        {
                            //success to request
                            pI2CObj->state++;
                        }                        
                        
                    } else {
                        //no reading and no reading
                        return 0; //success because no need any action
                    }
                }                
                                
#else                
                return -1;
#endif
            }
            return 1; //continue
            break;
        case 1:
            //Done?
            if (1 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C1   
                if (transferStatus1 == APP_TRANSFER_STATUS_SUCCESS)
                {
                    /* successfully done I2C Action */                    
                    pI2CObj->state = 0;
                    Delay_1 = 0;
                    ReTry_1=0;
                    return 0; //success
                }
                else if (transferStatus1 == APP_TRANSFER_STATUS_ERROR)
                {
                    /* Error in I2C Transaction */                    
                    Delay_1++;
                    if (Delay_1 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_1 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_1=0;
                        ReTry_1=0;
                        return (-1); //fail
                    } else {
                        ReTry_1++;
                        pI2CObj->state = 0;
                        Delay_1 = 0;
                        return 1; //continue
                    }
                }              
                return 1; //continue
                
#else
                pI2CObj->state = 0;
                return -1;
#endif
            } else if (2 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C2     
                if (transferStatus2 == APP_TRANSFER_STATUS_SUCCESS)
                {
                    /* successfully done I2C Action */                    
                    pI2CObj->state = 0;
                    Delay_2 = 0;
                    ReTry_2=0;
                    return 0; //success
                }
                else if (transferStatus2 == APP_TRANSFER_STATUS_ERROR)
                {
                    /* Error in I2C Transaction */                    
                    Delay_2++;
                    if (Delay_2 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_2 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_2=0;
                        ReTry_2=0;
                        return (-1); //fail
                    } else {
                        ReTry_2++;
                        pI2CObj->state = 0;
                        Delay_2 = 0;
                        return 1; //continue
                    }
                }              
                return 1; //continue                                               
#else
                pI2CObj->state = 0;
                return -1;
#endif
            } else if (3 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C3 
                if (transferStatus3 == APP_TRANSFER_STATUS_SUCCESS)
                {
                    /* successfully done I2C Action */                    
                    pI2CObj->state = 0;
                    Delay_3 = 0;
                    ReTry_3=0;
                    return 0; //success
                }
                else if (transferStatus3 == APP_TRANSFER_STATUS_ERROR)
                {
                    /* Error in I2C Transaction */                    
                    Delay_3++;
                    if (Delay_3 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_3 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_3=0;
                        ReTry_3=0;
                        return (-1); //fail
                    } else {
                        ReTry_3++;
                        pI2CObj->state = 0;
                        Delay_3 = 0;
                        return 1; //continue
                    }
                }              
                return 1; //continue         
#else
                pI2CObj->state = 0;
                return -1;
#endif
            } else if (4 == pI2CObj->I2C_Peripheral_No) {
#ifdef USE_I2C4 
                if (transferStatus4 == APP_TRANSFER_STATUS_SUCCESS)
                {
                    /* successfully done I2C Action */                    
                    pI2CObj->state = 0;
                    Delay_4 = 0;
                    ReTry_4=0;
                    return 0; //success
                }
                else if (transferStatus4 == APP_TRANSFER_STATUS_ERROR)
                {
                    /* Error in I2C Transaction */                    
                    Delay_4++;
                    if (Delay_4 < I2C_DEVICE_TIMEOUT) return 1;
                    if (ReTry_4 >= I2C_MAX_RE_TRY) {
                        pI2CObj->state = 0;
                        Delay_4=0;
                        ReTry_4=0;
                        return (-1); //fail
                    } else {
                        ReTry_4++;
                        pI2CObj->state = 0;
                        Delay_4 = 0;
                        return 1; //continue
                    }
                }              
                return 1; //continue                  
                
#else           
                pI2CObj->state = 0;
                return -1;
#endif
            }

    }
    return 1;
}




