/*
  Name:    Sauron.cpp
  Author:  Roman Schönfeld & Daniel Hausherr
*/

/***************************************************************************************************
  This work is licensed under a Creative Commons Attribution 4.0 International (CC BY 4.0) License.

  You are free to:

  Share — copy and redistribute the material in any medium or format
  Adapt — remix, transform, and build upon the material
  for any purpose, even commercially.

  The licensor cannot revoke these freedoms as long as you follow the license terms.

  Under the following terms:

  Attribution — You must give appropriate credit, provide a link to the license, and indicate if
  changes were made. You may do so in any reasonable manner, but not in any way that suggests
  the licensor endorses you or your use.

  No additional restrictions — You may not apply legal terms or technological measures that
  legally restrict others from doing anything the license permits.

  Credits:
  Laboratory for Physics and Illumination Technologies,
  University of Applied Sciences Southern Westfalia
  Germany

  Make Light! Support by the BMBF in the development of this device is gratefully acknowledged
***************************************************************************************************/

#include "Sauron.h"

/***************************************************************************************************
**  Constanten
***************************************************************************************************/
static const int I2C_ADDRESS = 0x74;

/***************************************************************************************************
**  locale Functions
***************************************************************************************************/
static int8_t i2c_write_register_device(uint8_t*, uint8_t, uint8_t);
static int8_t i2c_read_register_device(uint8_t*, uint8_t, uint8_t, uint8_t);

/***************************************************************************************************
**  external Functions
***************************************************************************************************/
// Initialisiert
void init_device() {

  Wire.begin();

  current_data_output = CH1;
  current_ref = IREF_NA_5120;
  current_time = TINT_MS_64;

  set_config_mcdc04();
}


// Verarbeitet Parameter und schreibt sie ins Register
int8_t set_config_mcdc04() {
  uint8_t configx[] = { _CREGL, 0,  _CMD };

  // versetzt den Mazet in den Configmodus
  if (set_mode(false) != 0) // ungleich 0 no succes
    return -1;

  configx[1] = (((uint8_t)current_ref << 4) | (uint8_t)current_time);
  configx[1] = (configx[1] | 0x80); // setze die Anado der Photodiode an den Eingang des ICs

  if (i2c_write_register_device(configx, sizeof(configx), I2C_ADDRESS))  // ungleich 0 no succes
    return -1;

  delay(10);

  return 0;
}

// Beginnt oder Beendet Messung
int8_t set_mode(bool mode) {
  uint8_t configx[] = { _OSR, 0};

  if (mode)
    configx[1] = _MEASUREMENT_MODE;
  else
    configx[1] = _CONFIG_MODE;

  if (i2c_write_register_device(configx, sizeof(configx), I2C_ADDRESS)) // ungleich 0 no succes
    return -1;

  delay(10);

  return 0;
}

// Zum Auslesen der Messdaten
int8_t raw_counts(uint16_t *value, uint8_t b_size) {
  uint8_t data[b_size * 2];

  if (i2c_read_register_device(data, sizeof(data), (uint8_t)current_data_output, I2C_ADDRESS) != 0) // ungleich 0 no succes
    return -1;

  for (uint8_t i = 0; i < b_size; i++)
    value[i] = (uint16_t)(((data[(2 * i) + 1]) << 8) | data[2 * i]);

  return 0;
}

// Zum Auslesen der Register
int8_t get_config(uint16_t *value, uint8_t b_size) {
  uint8_t data[b_size], i = 0;

  if (i2c_read_register_device(data, b_size, _CREGL, I2C_ADDRESS) != 0) // ungleich 0 no succes
    return -1;

  for (i = 0; i < b_size; i++)
    value[i] = (uint16_t)data[i];

  return 0;
}

int8_t define_matching_range() {

  bool loop_end = false;
  uint8_t save_time = 0;
  uint16_t ready_pin_timeouts = 0, counts_current = 0, max_value = 0, min_value = 0, time_offset = 1024;

  save_time = (uint8_t)current_time;
  current_time = (integrationTime)TINT_MS_4;

  max_value = (uint16_t)(pow(2.0d, (double)current_time) + 0.5d) * time_offset;
  max_value = (uint16_t)(((double)max_value /  100.0d) * 90.0d);
  min_value =  (uint16_t)(((double)max_value  / 100.0d) * 10.0d);
  
  while (!loop_end) {

    if (set_config_mcdc04() != 0) {
      return -1;
    }
    
    if (set_mode(true) == 0) {  // Parameter TRUE falls Messung starten und FALSE falls Messung beenden
      while (analogRead(_READY_PIN) <= 10) {
        ready_pin_timeouts++;
        if (ready_pin_timeouts > (uint16_t)((pow(2.0d, (double)current_time) + 0.5d) * 4)) {
          return -1;
        }
        else {
          delay(1);
        }
      }

      ready_pin_timeouts = 0;
    }
    else {
      return -1;
    }

    if (raw_counts(&counts_current, 1) == 0) {
      if (counts_current >= max_value) {
        if ((uint8_t)current_ref < 4)
          current_ref = (referenceCurrent)((uint8_t)current_ref + 1);
        else if ((uint8_t)current_ref == 4)
          loop_end = true;
      }
      else {
        if (counts_current < min_value) {
          if ((uint8_t)current_ref > 0)
            current_ref = (referenceCurrent)((uint8_t)current_ref - 1);
          if ((uint8_t)current_ref == 0)
            loop_end = true;
        }
        else
          loop_end = true;
      }

      if (loop_end)
        break;

    } else {
      return -1;
    }
  } // end while

  current_time = (integrationTime)save_time;

  if (set_config_mcdc04() != 0) {
    return -1;
  }

  return 0;
}


/***************************************************************************************************
**  internal Functions
***************************************************************************************************/
/***************************************************************************************************
**  i2c_write_register_sauron
**
**  schreibt in die Register des Saurons
**
**  Input:   command Buffer beinhaltet die Registerwerte, size Größe des Buffers
**  Output: -1 - No Succes
**       0 - Succes
***************************************************************************************************/
static int8_t i2c_write_register_device(uint8_t *conf, uint8_t b_size, uint8_t address ) {

  Wire.beginTransmission(address);
  Wire.write(conf, b_size);

  return (int8_t)Wire.endTransmission(); // ungleich 0 no succes
}

/***************************************************************************************************
**  i2c_read_register_sauron
**
**  liest aus den Registern des Saurons
**
**  Input:   value Buffer zum einlesen der Werte, t_size Größe des Buffers, register_number Register Nummer aus dem gelesen werden soll
**  Output: -1 - No Succes
**       0 - Succes
***************************************************************************************************/
static int8_t i2c_read_register_device(uint8_t *data, uint8_t b_size, uint8_t reg_number, uint8_t address) {
  uint16_t i2c_read_timeouts = 0, i = 0;
  uint8_t reg[] = { reg_number };

  if (i2c_write_register_device(reg, sizeof(reg), address) != 0) // ungleich 0 no succes
    return -1;

  Wire.requestFrom(address, b_size);

  while (Wire.available() < b_size) {
    i2c_read_timeouts++;
    if (i2c_read_timeouts > 100) {
      return -1;
    }
    else {
      delay(1);
    }
  }
  for (i = 0; i < b_size; i++) {
    data[i] = (uint8_t)Wire.read();
  }
  return 0;
}
