//
//    FILE: m5rotate8.cpp
//  AUTHOR: Rob Tillaart
// VERSION: 0.4.1
// PURPOSE: Arduino library for M5 8ROTATE 8x rotary encoders
//     URL: https://github.com/RobTillaart/M5ROTATE8


#include "m5rotate8.h"

//  FIRMWARE V1 REGISTERS
#define M5ROTATE8_REG_ADDRESS               0xFF
#define M5ROTATE8_REG_VERSION               0xFE
#define M5ROTATE8_REG_BASE_ABS              0x00
#define M5ROTATE8_REG_BASE_REL              0x20
#define M5ROTATE8_REG_BASE_RESET            0x40
#define M5ROTATE8_REG_BASE_BUTTON_VALUE     0x50
#define M5ROTATE8_REG_SWITCH                0x60
#define M5ROTATE8_REG_RGB                   0x70

//  FIRMWARE V2 REGISTERS
#define M5ROTATE8_REG_BASE_BUTTON_TOGGLE    0x58
#define M5ROTATE8_REG_ENCODER_MASK          0x61
#define M5ROTATE8_REG_BUTTON_MASK           0x62



M5ROTATE8::M5ROTATE8(uint8_t address, TwoWire *wire)
{
  _address = address;
  _wire = wire;
}


bool M5ROTATE8::begin()
{
  if (! isConnected()) return false;
  return true;
}


bool M5ROTATE8::isConnected()
{
  _wire->beginTransmission(_address);
  return (_wire->endTransmission() == 0);
}


bool M5ROTATE8::setAddress(uint8_t address)
{
  if ((address < 8) || (address > 119)) return false;
  _address = address;
  write8(M5ROTATE8_REG_ADDRESS, _address);
  return isConnected();
}


uint8_t M5ROTATE8::getAddress()
{
  return _address;
}


uint8_t M5ROTATE8::getVersion()
{
  return read8(M5ROTATE8_REG_VERSION);
}


//
//  ROTARY ENCODER PART
//
int32_t M5ROTATE8::getAbsCounter(uint8_t channel)
{
  return read32(M5ROTATE8_REG_BASE_ABS + (channel << 2));
}


bool M5ROTATE8::setAbsCounter(uint8_t channel, int32_t value)
{
  return write32(M5ROTATE8_REG_BASE_ABS + (channel << 2), value);
}


int32_t M5ROTATE8::getRelCounter(uint8_t channel)
{
  return read32(M5ROTATE8_REG_BASE_REL + (channel << 2));
}


bool M5ROTATE8::getKeyPressed(uint8_t channel)
{
  if (channel > 7)
  {
    return false;
  }
  return (0 == read8(M5ROTATE8_REG_BASE_BUTTON_VALUE + channel));
}


bool M5ROTATE8::resetCounter(uint8_t channel)
{
  if (channel > 7)
  {
    return false;
  }
  write8(M5ROTATE8_REG_BASE_RESET + channel, 1);
  return true;
}


void M5ROTATE8::resetAll()
{
  for (int channel = 0; channel < 8; channel++)
  {
    write8(M5ROTATE8_REG_BASE_RESET + channel, 1);
  }
}


//
//  INPUT SWITCH PART
//
uint8_t M5ROTATE8::inputSwitch()
{
  return read8(M5ROTATE8_REG_SWITCH);
}


//
//  LED PART
//
bool M5ROTATE8::writeRGB(uint8_t channel, uint8_t R, uint8_t G, uint8_t B)
{
  if (channel > 8)
  {
    return false;
  }
  write24(M5ROTATE8_REG_RGB + (channel * 3), R, G, B);
  return true;
}


uint32_t M5ROTATE8::readRGB(uint8_t channel)
{
  return read24(M5ROTATE8_REG_RGB + (channel * 3));
}


bool M5ROTATE8::setAll(uint8_t R, uint8_t G, uint8_t B)
{
  for (uint8_t ch = 0; ch < 9; ch++)
  {
    write24(M5ROTATE8_REG_RGB + (ch * 3), R, G, B);
  }
  return true;
}


bool M5ROTATE8::allOff()
{
  return setAll(0, 0, 0);
}



//
//  FIRMWARE V2
//
bool M5ROTATE8::setButtonToggleCount(uint8_t channel, uint8_t value)
{
  if (channel > 7)
  {
    return false;
  }
  return write8(M5ROTATE8_REG_BASE_BUTTON_TOGGLE + channel, value);
}


uint8_t M5ROTATE8::getButtonToggleCount(uint8_t channel)
{
  if (channel > 7)
  {
    return 0;
  }
  return read8(M5ROTATE8_REG_BASE_BUTTON_TOGGLE + channel);
}


//  0 = no change, 1 = changed
uint8_t M5ROTATE8::getEncoderChangeMask()
{
  return read8(M5ROTATE8_REG_ENCODER_MASK);
}


//  0 = not pressed, 1 = pressed (inverted the datasheetV2 specification)
//  seems more logical
uint8_t M5ROTATE8::getButtonChangeMask()
{
  //  invert register to be more logical IMHO.
  return read8(M5ROTATE8_REG_BUTTON_MASK) ^ 0xFF;
}


//////////////////////////////////////////////////////////////////////////////
//
//  PRIVATE
//
bool M5ROTATE8::write8(uint8_t reg, uint8_t value)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _wire->write(value);
  _error = _wire->endTransmission();
  return (_error == 0);
}


uint8_t M5ROTATE8::read8(uint8_t reg)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _error = _wire->endTransmission();
  if (_error != 0)
  {
    //  error handling
    return 0;
  }
  if (_wire->requestFrom(_address, (uint8_t)1) != 1)
  {
    //  error handling
    return 0;
  }
  return _wire->read();
}


bool M5ROTATE8::write24(uint8_t reg, uint8_t R, uint8_t G, uint8_t B)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _wire->write(R);
  _wire->write(G);
  _wire->write(B);
  _error = _wire->endTransmission();
  return (_error == 0);
}


uint32_t M5ROTATE8::read24(uint8_t reg)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _error = _wire->endTransmission();
  if (_error != 0)
  {
    //  error handling
    return 0;
  }
  if (_wire->requestFrom(_address, (uint8_t)3) != 3)
  {
    //  error handling
    return 0;
  }
  uint32_t value = 0;
  value += _wire->read();
  value <<= 8;
  value += _wire->read();
  value <<= 8;
  value += _wire->read();
  return value;
}


bool M5ROTATE8::write32(uint8_t reg, uint32_t value)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  //  explicit casting to solve ambiguity #8
  _wire->write((uint8_t)(value & 0xFF));
  value >>= 8;
  _wire->write((uint8_t)(value & 0xFF));
  value >>= 8;
  _wire->write((uint8_t)(value & 0xFF));
  value >>= 8;
  _wire->write((uint8_t)(value & 0xFF));
  _error = _wire->endTransmission();
  return (_error == 0);
}


uint32_t M5ROTATE8::read32(uint8_t reg)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _error = _wire->endTransmission();
  if (_error != 0)
  {
    //  error handling
    return 0;
  }
  if (_wire->requestFrom(_address, (uint8_t)4) != 4)
  {
    //  error handling
    return 0;
  }
  uint32_t value = 0;
  value += (_wire->read());
  value += (((uint32_t)_wire->read()) << 8 );
  value += (((uint32_t)_wire->read()) << 16);
  value += (((uint32_t)_wire->read()) << 24);
  return value;
}


//  -- END OF FILE --
