#pragma once

namespace EEPROM_AT24C
{
  
enum class EEType { AT24C01=0, AT24C02=1, AT24C04=2, AT24C08=3, AT24C16=4, 
                    AT24C32=5, AT24C64=6, AT24C128=7, AT24C256=8, AT24C512=9, AT24CM01=10, AT24CM02=11 };

using EEParams = struct
{
  uint32_t size;
  uint32_t page_size;
};

static constexpr EEParams AllParams[12] =  //Объём памяти и размер страницы для всех чипов 24Cxx
{ {128,8}, {256,8}, {512,16}, {1024*1,16}, {1024*2,16}, 
  {1024*4,38}, {1024*8,32}, {1024*16,64}, {1024*32,64}, {1024*65,128}, {1024*128,256}, {1024*256,256} };

template<typename TI2C, typename TDelay, EEType eeType, uint8_t HW_ADR>
class EE_24C
{
  public:    
    _inline_ EE_24C(){};
    
    static _inline_ bool ReadByte(uint16_t adr, uint8_t *data)
    {
      if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24CM01))
      { //Микросхемы с двухбайтовой адресацией  + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>16)&0x3)<<1);
        return TI2C::ReadReg(new_adr,(uint16_t)adr, data, (uint8_t)1);
      }
      else if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24C32))        
      { //Микросхемы с двухбайтовой адресацией 
        return TI2C::ReadReg(hw_adr,(uint16_t)adr, data, (uint8_t)1);
      }
      else if constexpr(uint32_t(eeType)<=uint32_t(EEType::AT24C02))
      { //Микросхемы с однобайтовой адресацией  
        return TI2C::ReadReg(hw_adr,(uint8_t)adr, data, (uint8_t)1);
      }
      else
      { //Микросхемы с адресацией 1 байт + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>8)&0x3)<<1);
        return TI2C::ReadReg(new_adr,(uint8_t)adr, data, (uint32_t)1);     
      }
    }
        
    static _inline_ bool WriteByte(uint16_t adr, uint8_t data)
    {
      if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24CM01))
      { //Микросхемы с двухбайтовой адресацией  + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>16)&0x3)<<1);
        return TI2C::WriteReg(new_adr,(uint16_t)adr, &data, (uint8_t)1);
      }
      else if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24C32))
      { //Микросхемы с двухбайтовой адресацией  
        return TI2C::WriteReg(hw_adr,(uint16_t)adr, &data, (uint8_t)1); 
      }
      else if constexpr(uint32_t(eeType)<=uint32_t(EEType::AT24C02))
      { //Микросхемы с однобайтовой адресацией  
        return TI2C::WriteReg(hw_adr,(uint8_t)adr, &data, (uint8_t)1);
      }
      else
      { //Микросхемы с адресацией 1 байт + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>8)&0x3)<<1);
        return TI2C::WriteReg(new_adr,(uint8_t)adr, &data, (uint8_t)1);      
      }      
    }
    
    static _inline_ bool ReadData(uint32_t adr, uint8_t *data, uint32_t length)
    {
      if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24CM01))
      { //Микросхемы с двухбайтовой адресацией  + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>16)&0x3)<<1);
        return TI2C::ReadReg(new_adr,(uint16_t)adr, data, (uint32_t)length);
      }
      else if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24C32))        
      { //Микросхемы с двухбайтовой адресацией         
        return TI2C::ReadReg(hw_adr, (uint16_t)adr, data, (uint32_t)length);
      }
      else if constexpr(uint32_t(eeType)<=uint32_t(EEType::AT24C02))
      { //Микросхемы с однобайтовой адресацией  
        return TI2C::ReadReg(hw_adr,(uint8_t)adr, data, (uint8_t)length);
      }
      else
      { //Микросхемы с адресацией 1 байт + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>8)&0x3)<<1);
        return TI2C::ReadReg(new_adr,(uint8_t)adr, data, (uint32_t)length);     
      }
    }
    
    static _inline_ bool WriteData(uint32_t adr, uint8_t *data, uint32_t length)
    {
      if( (((uint32_t)adr) + ((uint32_t)length)) > eeparams.size) return false; 
      uint32_t page_shift = adr&(eeparams.page_size-1);
      //Шаг 1: Запись начального кусочка
      uint32_t data2write = length<(eeparams.page_size-page_shift) ? length : (eeparams.page_size-page_shift);
      if(!WriteDataInOnePage(adr,data,data2write)) return false;   
      length -= data2write;
      adr+=data2write;
      data+=data2write;
      //Шаг 2: Запись остальных страниц (они уже выровнены)
      while(length)
      {
        TDelay::template Delay_ms<5>(); //Ждать пока предыдущая страница не запишется
        data2write = length<eeparams.page_size ? length : eeparams.page_size;
        if(!WriteDataInOnePage(adr,data,data2write)) return false;
        length -= data2write;
        adr+=data2write;
        data+=data2write;
      }
      return true;
    }
    
  private:    
    static constexpr uint8_t hw_adr=0xA0+((HW_ADR&0x7)<<1); 
    static constexpr EEParams eeparams  = AllParams[uint32_t(eeType)];
    
    static _inline_ bool WriteDataInOnePage(uint32_t adr, uint8_t *data, uint32_t length)
    {
      if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24CM01))
      { //Микросхемы с двухбайтовой адресацией  + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>16)&0x3)<<1);
        return TI2C::WriteReg(new_adr,(uint16_t)adr, data, (uint32_t)length);
      }
      else if constexpr (uint32_t(eeType)>=uint32_t(EEType::AT24C32))
      { //Микросхемы с двухбайтовой адресацией  
        if constexpr(eeparams.page_size>255)
          return TI2C::WriteReg(hw_adr,(uint16_t)adr, data, (uint32_t)length);
        else
          return TI2C::WriteReg(hw_adr,(uint16_t)adr, data, (uint8_t)length); 
      }
      else if constexpr(uint32_t(eeType)<=uint32_t(EEType::AT24C02))
      { //Микросхемы с однобайтовой адресацией  
        return TI2C::WriteReg(hw_adr,(uint8_t)adr, data, (uint8_t)length);
      }
      else
      { //Микросхемы с адресацией 1 байт + биты в HW_ADR
        uint8_t new_adr = hw_adr + (((adr>>8)&0x3)<<1);
        return TI2C::WriteReg(new_adr,(uint8_t)adr, data, (uint8_t)length);      
      }
    }      
};

} // namespace EEPROM_AT24C