#include "DFRobot_IF.h"
#include <DFRobot_Type.h>

#define DEFAULT_IIC_FREQ  100000L//Except ESP32, AVR, ESP8266, M0 are all 1khz

uint8_t interfaceComHardwareIIC(sGdlIF_t *p, uint8_t cmd, uint8_t *pBuf, uint32_t len)
{
  //Serial.print("cmd = ");Serial.println(cmd);
  if((p == NULL))
      return 0;
  switch(cmd){
      case IF_COM_PROTOCOL_INIT:
      {
          //Serial.println("IF_COM_PROTOCOL_INIT");
          if(p->pro.iic == NULL) p->pro.iic = &Wire;
          p->pro.iic->begin();
          /* When the frequency is 0, the onboard frequency is used by default, and the frequency is assigned to p-> freq */
          if(p->freq != 0) {
              p->pro.iic->setClock(p->freq);
          }else{
              p->freq = DEFAULT_IIC_FREQ;
          }
          p->isBegin = true;
      }
           break;
      case IF_COM_PROTOCOL_INIT1:
      {
          //Serial.println("IF_COM_PROTOCOL_INIT");
          if(p->pro.iic == NULL) p->pro.iic = &Wire;
          //p->pro.iic->begin();
          /* When the frequency is 0, the onboard frequency is used by default, and the frequency is assigned to p-> freq */
          if(p->freq != 0) {
              p->pro.iic->setClock(p->freq);
          }else{
              p->freq = DEFAULT_IIC_FREQ;
          }
          p->isBegin = true;
      }
           break;
      case IF_COM_SET_FREQUENCY:
      {
           if(p->freq == 0) return 0;
           p->pro.iic->setClock(p->freq);
      }
           break;
      case IF_COM_WRITE_CMD:
      {
          if(!p->isBegin) return 0;
          while(len){
              p->pro.iic->beginTransmission(p->pinList[IF_PIN_ADDR]);
              uint32_t n = 0;
              if(p->dev->devName == DEV_TYPE_SCREEN)
              {
                  (len > I2C_BUFFER_LENGTH/2) ? n = I2C_BUFFER_LENGTH/2 : n = len;
                  p->pro.iic->write(0x00);
              }else{
                  (len > I2C_BUFFER_LENGTH) ? n = len : n = I2C_BUFFER_LENGTH;
              }

              /*
              p->pro.iic->write(pBuf, n); 
              
              ISSUE:
              There is an compilation errordue to an ambiguity in the function write call when using with 
              the Arduino Nano 33 BLE, which is based on the MBED OS. 
              The "write" is overloaded and the compiler can't decide between arduino::MbedI2C::write(const uint8_t*, int) 
              and arduino::Print::write(const uint8_t*, size_t).
              
              SOLUTION:
              Cast the second argument to the correct type, assuming uint32_t as the datatype.
              Cast it to int or size_t depending on the appropriate method. 
              If you're writing to an I2C device, you probably want to use the arduino::MbedI2C::write method.
              p->pro.iic->write(pBuf, static_cast<int>(n))
              */
              
              #if defined(ARDUINO_ARCH_NRF52840) || defined(ARDUINO_NRF52_ADAFRUIT)
                // Modified write call for Arduino Nano 33 BLE/Sense, Adafruit nRF52 boards, Seeed Xiao BLE/Sense boards
                p->pro.iic->write(pBuf, static_cast<int>(n));
              #else
                // Original write call for other boards
                p->pro.iic->write(pBuf, n);
              #endif
              p->pro.iic->endTransmission();
              pBuf += n;
              len -= n;
          }
           break;
      }
      case IF_COM_READ_DATA:
      {
          if(!p->isBegin) return 0;
          if((pBuf[0] > 4) || (len > I2C_BUFFER_LENGTH)) return 0;
          p->pro.iic->beginTransmission(p->pinList[IF_PIN_ADDR]);
          p->pro.iic->write((const uint8_t *)(pBuf+1),(size_t)pBuf[0]);
          if( p->pro.iic->endTransmission() != 0){
                return 0;
          }
          p->pro.iic->requestFrom(p->pinList[IF_PIN_ADDR], len);
          for(uint16_t i = 0; i < len; i++){
              pBuf[i] = p->pro.iic->read();
          }
      }
          break;
      case IF_COM_WRITE_FLASH_FIXED:
      {
          Serial.println("IF_COM_WRITE_FLASH_FIXED");
          if(!(p->isBegin) || pBuf[0] > 4) return 0;
          while(len){
              p->pro.iic->beginTransmission(p->pinList[IF_PIN_ADDR]);
              uint32_t n = 0;
              if(p->dev->devName == DEV_TYPE_SCREEN)
              {
                  (len > (I2C_BUFFER_LENGTH - 1)/pgm_read_byte(&pBuf[0])) ? n = (I2C_BUFFER_LENGTH - 1)/pgm_read_byte(&pBuf[0]) : n = len;
                  p->pro.iic->write(0x40);
              }else{
                  (len > I2C_BUFFER_LENGTH/pgm_read_byte(&pBuf[0])) ? n = I2C_BUFFER_LENGTH/pgm_read_byte(&pBuf[0]) : n = len;
              }
              for(uint32_t i = 0; i < n; i++){
                  p->pro.iic->write(pgm_read_byte(&pBuf[1]));
                  if(pBuf[0] > 1)
                      p->pro.iic->write(pgm_read_byte(&pBuf[2]));
                  else if(pBuf[0] > 2)
                      p->pro.iic->write(pgm_read_byte(&pBuf[3]));
                  else if(pBuf[0] > 3)
                      p->pro.iic->write(pgm_read_byte(&pBuf[4]));
                  #if defined(ESP32)
                  p->pro.iic->flush();
                  #endif
              }
              len -= n;
              p->pro.iic->endTransmission();
          }
      }
           break;
      case IF_COM_WRITE_FLASH_INC:
      {
          Serial.println("IF_COM_WRITE_FLASH_INC");
          if(!(p->isBegin)) return 0;
          while(len){
              p->pro.iic->beginTransmission(p->pinList[IF_PIN_ADDR]);
              uint32_t n = 0;
              if(p->dev->devName == DEV_TYPE_SCREEN)
              {
                  (len > (I2C_BUFFER_LENGTH - 1)) ? n = I2C_BUFFER_LENGTH - 1 : n = len;
                  p->pro.iic->write(0x40);
              }else{
                  (len > I2C_BUFFER_LENGTH) ? n = I2C_BUFFER_LENGTH : n = len;
              }
              for(uint32_t i = 0; i < n; i++){
                  p->pro.iic->write(pgm_read_byte(pBuf));
                  pBuf++;
              }
              len -= n;
              p->pro.iic->endTransmission();
          }
           break;
      }
      case IF_COM_WRITE_RAM_FIXED:
      {
          Serial.println("IF_COM_WRITE_RAM_FIXED");
          if(!(p->isBegin)) return 0;
          while(len){
              p->pro.iic->beginTransmission(p->pinList[IF_PIN_ADDR]);
              uint32_t n = 0;
              if(p->dev->devName == DEV_TYPE_SCREEN)
              {
                  (len > ((I2C_BUFFER_LENGTH-1)/pBuf[0])) ? n = ((I2C_BUFFER_LENGTH-1)/pBuf[0]) : n = len;
                  p->pro.iic->write(0x40);
              }else{
                  (len > (I2C_BUFFER_LENGTH)/pBuf[0]) ? n = I2C_BUFFER_LENGTH/pBuf[0] : n = len;
              }
              for(uint32_t i = 0; i < n; i++){
                  if(pBuf[0] < 5){
                      p->pro.iic->write(pBuf[1]);
                      if(pBuf[0] > 1)
                          p->pro.iic->write(pBuf[2]);
                      else if(pBuf[0] > 2)
                          p->pro.iic->write(pBuf[3]);
                      else if(pBuf[0] > 3)
                          p->pro.iic->write(pBuf[4]);
                  }else{
                      for(uint8_t j = 0; j < pBuf[0]; j++){
                          p->pro.iic->write(pBuf[1+j]);
                      }
                  }
              }
              len -= n;
              p->pro.iic->endTransmission();
          }
      }
           break;
      case IF_COM_WRITE_RAM_INC:
      {
          //Serial.println("IF_COM_WRITE_RAM_INC");
          if(!(p->isBegin)) return 0;
          while(len){
              p->pro.iic->beginTransmission(p->pinList[IF_PIN_ADDR]);
              uint32_t n = 0;
			  if(p->dev->devName == DEV_TYPE_SCREEN)
              {
                  (len > I2C_BUFFER_LENGTH - 1) ? n = I2C_BUFFER_LENGTH - 1 : n = len;
                  p->pro.iic->write(0x40);
                  for(uint32_t i = 0; i < n; i++){
                      p->pro.iic->write(pBuf[i]);
                  }
                  pBuf += n;
                  len -= n;
                  p->pro.iic->endTransmission();
              }else{
                  p->pro.iic->write((const uint8_t *)(pBuf+1),(size_t)pBuf[0]);
                  if(len > I2C_BUFFER_LENGTH) return 0;

                  /*
                  p->pro.iic->write(&pBuf[(1+pBuf[0])],len);
                  
                  ISSUE:
                  There is an compilation errordue to an ambiguity in the function write call when using with 
                  the Arduino Nano 33 BLE, which is based on the MBED OS. 
                  The "write" is overloaded and the compiler can't decide between arduino::MbedI2C::write(const uint8_t*, int) 
                  and arduino::Print::write(const uint8_t*, size_t).
                  
                  SOLUTION:
                  Cast the len variable to the appropriate type
                  */
                  #if defined(ARDUINO_ARCH_NRF52840) || defined(ARDUINO_NRF52_ADAFRUIT)
                    // Modified write call for Arduino Nano 33 BLE/Sense, Adafruit nRF52 boards, Seeed Xiao BLE/Sense boards
                    p->pro.iic->write(&pBuf[(1+pBuf[0])], static_cast<int>(len));
                  #else
                    // Original write call for other boards
                    p->pro.iic->write(&pBuf[(1+pBuf[0])],len);
                  #endif

                  if( p->pro.iic->endTransmission() != 0) return 0;
                  len -= len;
              }
          }
      }
           break;
      default:
           break;
  }
  return 1;
}
