// Headers
#include "PowerMeter.h"

/**
 * get_VRMS function
 * This function return the value of the VRMS of the selected channel
 */
uint32_t get_VRMS(PowerMeterChannel channel)
{
  _infoCommand command = VRMS;

  return _returnPowerInfoUint32((_infoCommand)(command + (channel << 2)));

}

/**
 * get_IRMS function
 * This function return the value of the IRMS of the selected channel
 */
uint32_t get_IRMS(PowerMeterChannel channel)
{
  _infoCommand command = IRMS;

  return _returnPowerInfoUint32((_infoCommand)(command + (channel << 2)));

}

/**
 * get_VPEAK function
 * This function return the value of the VPEAK of the selected channel
 */
uint32_t get_VPEAK(PowerMeterChannel channel)
{
  _infoCommand command = VPEAK;

  return _returnPowerInfoUint32((_infoCommand)(command + (channel << 2)));

}

/**
 * get_IPEAK function
 * This function return the value of the IPEAK of the selected channel
 */
uint32_t get_IPEAK(PowerMeterChannel channel)
{
  _infoCommand command = IPEAK;

  return _returnPowerInfoUint32((_infoCommand)(command + (channel << 2)));

}

/**
 * get_PowerFactor function
 * This function return the value of the power factor for the selected channel
 */
uint32_t get_PowerFactor(PowerMeterChannel channel)
{
  _infoCommand command = POWER_FACTOR;

  return _returnPowerInfoUint32((_infoCommand)(command + (channel << 2)));

}

/**
 * get_Frequency function
 * This function return the frequency for the selected channel
 */
uint16_t get_Frequency(PowerMeterChannel channel)
{
  _infoCommand command = FREQUENCY;

  return _returnPowerInfoUint16((_infoCommand)(command + (channel << 1)));

}

/**
 * get_ActivePower function
 */
int64_t get_ActivePower(PowerMeterChannel channel)
{
  _infoCommand command = ACTIVE_POWER;

  return _returnPowerInfoInt64((_infoCommand)(command + (channel << 3)));

}

/**
 * get_ReactivePower function
 */
int64_t get_ReactivePower(PowerMeterChannel channel)
{
  _infoCommand command = REACTIVE_POWER;

  return _returnPowerInfoInt64((_infoCommand)(command + (channel << 3)));

}
/**
 * get_ApparentPower function
 */
int64_t get_ApparentPower(PowerMeterChannel channel)
{
  _infoCommand command = APPARENT_POWER;

  return _returnPowerInfoInt64((_infoCommand)(command + (channel << 3)));

}

/**
 * _returnPowerInfoUint32 function
 * This function return the value of the selected data position keeping in mind the lenght of the information
 */

uint32_t _returnPowerInfoUint32(_infoCommand dataPosition)
{
  // Define variables
  int file;
  char filename[40];
  int data;
  char buf[5];
  char channel[2] = { 0 , dataPosition };

  // Open the I2C port
  Wire.beginTransmission(119);

  Wire.write((const uint8_t*)channel, 2);
  if (Wire.endTransmission() != 0)
    {
      Serial.println("Failed to write to the i2c bus.");
      return 0;
    }

  Wire.requestFrom(119, 5);
  if (Wire.available() != 5)
    {
      Serial.print("Failed to read from the i2c bus.");
      return 0;
    }

  for (int i = 0; i < 5; i++) {
    buf[i] = Wire.read();
  }

  data = (uint32_t)(buf[4] << 24) + (uint32_t)(buf[3] << 16) + (uint32_t)(buf[2] << 8) + (uint32_t)(buf[1]);

  // Return the data value
  return data;
}

/**
 * _returnPowerInfoUint32 function
 * This function return the value of the selected data position keeping in mind the lenght of the information
 */
uint16_t _returnPowerInfoUint16(_infoCommand dataPosition)
{
  // Define variables
  int file;
  char filename[40];
  int data;
  char buf[3];
  char channel[2] = { 0 , dataPosition };

  // Open the I2C port
  Wire.beginTransmission(119);

  Wire.write((const uint8_t*)channel, 2);
  if (Wire.endTransmission() != 0)
    {
      Serial.print("Failed to write to the i2c bus.");
      return 0;
    }

  Wire.requestFrom(119, 3);
  if (Wire.available() != 3)
    {
      Serial.print("Failed to read from the i2c bus.");
      return 0;
    }

  for (int i = 0; i < 3; i++) {
    buf[i] = Wire.read();
  }

  data = (uint16_t)(buf[2]<<8)+(uint16_t)(buf[1]);

  // Return the data value
  return data;
}

/**
 * _returnPowerInfoUint64 function
 * This function return the value of the selected data position keeping in mind the lenght of the information
 */
uint64_t _returnPowerInfoUint64(_infoCommand dataPosition)
{
  // Define variables
  int file;
  char filename[40];
  uint64_t data;
  char buf[9];
  char channel[2] = { 0 , dataPosition };

  // Open the I2C port
  Wire.beginTransmission(119);

  Wire.write((const uint8_t*)channel, 2);
  if (Wire.endTransmission() != 0)
    {
      Serial.print("Failed to write to the i2c bus.");
      return 0;
    }

  Wire.requestFrom(119, 9);
  if (Wire.available() != 9)
    {
      Serial.print("Failed to read from the i2c bus.");
      return 0;
    }

  for (int i = 0; i < 9; i++) {
    buf[i] = Wire.read();
  }

  data = (uint64_t)((uint64_t)buf[8]<<56)+(uint64_t)((uint64_t)buf[7]<<48)+(uint64_t)((uint64_t)buf[6]<<40)+(uint64_t)((uint64_t)buf[5]<<32)+(uint64_t)((uint64_t)buf[4]<<24)+(uint64_t)((uint64_t)buf[3]<<16)+(uint64_t)((uint64_t)buf[2]<<8)+(uint64_t)((uint64_t)buf[1]);

  // Return the data value
  return data;
}

/**
 * _returnPowerInfoInt64 function
 * This function return the value of the selected data position keeping in mind the lenght of the information
 */
int64_t _returnPowerInfoInt64(_infoCommand dataPosition)
{
  // Define variables
  int file;
  char filename[40];
  int64_t data;
  char buf[9];
  char channel[2] = { 0 , dataPosition };

  // Open the I2C port
  Wire.beginTransmission(119);

  Wire.write((const uint8_t*)channel, 2);

  if (Wire.endTransmission() != 0)
    {
      Serial.print("Failed to write to the i2c bus.");
      return 0;
    }

  Wire.requestFrom(119, 9);
  if (Wire.available() != 9)
    {
      Serial.print("Failed to read from the i2c bus.");
      return 0;
    }

  for (int i = 0; i < 9; i++) {
    buf[i] = Wire.read();
  }

  data = (int64_t)((int64_t)buf[8]<<56)|(int64_t)((int64_t)buf[7]<<48)|(int64_t)((int64_t)buf[6]<<40)|(int64_t)((int64_t)buf[5]<<32)|(int64_t)((int64_t)buf[4]<<24)|(int64_t)((int64_t)buf[3]<<16)|(int64_t)((int64_t)buf[2]<<8)|(int64_t)((int64_t)buf[1]);

  // Return the data value
  return data;
}

/**
 * _returnPowerInfoInt64 function
 * This functions returns all the raw information saved by the power meter
 */
uint8_t get_AllInfo(uint8_t *info)
{
  // Define variables
  int file;
  char filename[40];
  int64_t data;
  char buf[8];
  char channel[2] = { 0 , 0 };

  // Open the I2C port
  Wire.beginTransmission(119);

  Wire.write((const uint8_t*)channel, 2);
  if (Wire.endTransmission() != 0)
    {
      // The write operation has failed
      Serial.print("Failed to write to the i2c bus.");
      return 1;
    }

  Wire.requestFrom(119, 139);

  if (Wire.available() != 139)
    {
      Serial.print("Failed to read from the i2c bus.");
      return 1;
    }

  for (int i = 0; i < 139; i++) {
    info[i] = Wire.read();
  }

  // Return the data value
  return 0;
}

/**
 * selectCom function
 * This function select if the RS232 from the internal power meter will be listened by the PLC or will be send to the  RS232 via external connector
 * If this function is not call, the definited communication will be with the RS232
 */
uint8_t selectCom(PowerMeterCommunication com)
{
  // Open the I2C port
  Wire.beginTransmission(119);

  Wire.write(com);
  if (Wire.endTransmission() != 0)
    {
      // The write operation has failed
      Serial.print("Failed to write to the i2c bus.");
      return 1;
    }

  return 0;
}
