#include "motory.h"
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2561_U.h>

/*
 * Zadefinovanie základných stavou zariadenia: 
 * DEVICE_MEASUREMENT : zariadenie, je v stave meranie, v ktorom
 *                      prebieha krokovanie motorov, a preposielanie
 *                      hodnôt nameraných svetelným senzorom, 
 * DEVICE_NO_MEASUREMENT : zariadenie je v stave pokoja, kedy v čaká
 *                         na príkazy z používateľského rozhrania, 
 * 
 * Počiatočným stavom zariadenia, po spustení je stav DEVICE_NO_MEASUREMENT.
 */

#define DEVICE_MEASUREMENT "MEASUREMENT"
#define DEVICE_NO_MEASUREMENT "NOMEASUREMENT"
String device_Rutine = DEVICE_NO_MEASUREMENT;

/*
 * Vytvorenie objektov tried potrebných, pre správne fungovanie zariadenia. 
 */
 
MOTOR motor;
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);

/*
 * Zadefinovanie zakladných príkazov, a separačných znakov, v podobe konštánt.
 */

const String START_BUTTON = "START";
const String STOP_BUTTON = "STOP";
const String END_MEASUREMENT = "END";
const String DEVICE_ANSWEAR = "ANSOK";
const String SEPARATOR = ",";;
const String NEW_PLANE = "C";

/*
 * Zadefinovanie dodatočných konštánt, uplatňujúcich sa v priebehu, behu programu. 
 */
 
String pc_Action_Command;
int gamma_Step;
int cPlane_Step;
float distance;
float sensor_Value = 0;

/*
 * Inicializácia komunikácie, a periférií ...  
 */
 
void setup() {
  Serial.begin(9600);
  Wire.begin();
  senzor_Inicialisation();
}

/*
 * Výber príslušného stavu zariadenia, na základe príkazov, dodaních skrz sériovú
 * linku ... 
 */

void loop() {
  if (device_Rutine.equalsIgnoreCase(DEVICE_MEASUREMENT)) {
    measurement_Process();
  } else {
    device_Rutine = DEVICE_NO_MEASUREMENT;
    Serial.print(END_MEASUREMENT);
    Serial.print(SEPARATOR);
  }
}

/*
 * "serialEvent" - funkcia, uplatňujúca sa v prípade, príchodu údajov cez sériovú linku,
 * vyhodnocuje typ príkazu, a následne vykonáva postup, ktorý vedie, buď k procesu merania, 
 * alebo k stavu pokoja zariadenia, v závislosti na konkrétnom prichádzajúcom príkaze, 
 * z používateľského rozhrania ... 
 */

void serialEvent() {
  pc_Action_Command = Serial.readString();
  pc_Action_Command.toUpperCase();
  if  (pc_Action_Command.equalsIgnoreCase(START_BUTTON)) {
    Serial.print(DEVICE_ANSWEAR);
    Serial.print(SEPARATOR);
    delay(1000);
    gamma_Step = Serial.readString().toInt();
    cPlane_Step = Serial.readString().toInt();
    distance = Serial.readString().toFloat();
    distance *= distance;

    motor.Cplane_MotorInit(cPlane_Step);
    motor.Gamma_MotorInit(gamma_Step);
    if (tsl.begin()) {
      if (motor.find_refPosition_C_Motor() && motor.find_refPosition_Gamma_Motor()) {
        device_Rutine = DEVICE_MEASUREMENT;
      } else {
        device_Rutine = DEVICE_NO_MEASUREMENT;
      }
    } else {
      device_Rutine = DEVICE_NO_MEASUREMENT;
    }
  } else if (pc_Action_Command.equalsIgnoreCase(STOP_BUTTON)) {
    device_Rutine = DEVICE_NO_MEASUREMENT;
  } else {
    device_Rutine = DEVICE_NO_MEASUREMENT;
  }
}

/*
 * "measurement_Process" - samotný proces merania, a preposielania
 * hodnôt nameraných prístrojom, spolu s umiestňovaním zadefinovaných 
 * separačných znakov ... 
 */
 
void measurement_Process() {
  sensors_event_t event;
  tsl.getEvent(&event);
  sensor_Value = event.light;
  sensor_Value *= distance;

  if (motor.cMotor_Move() && !motor.getEndOf_MeassurementProcess()) {
    Serial.print(sensor_Value);
    Serial.print(SEPARATOR);
    Serial.print(NEW_PLANE);
    Serial.print(SEPARATOR);
    motor.setCplaneMove(false);
  }
  if (motor.gammaMotor_Move() && !motor.getEndOf_MeassurementProcess()) {
    Serial.print(sensor_Value);
    Serial.print(SEPARATOR);
    motor.setGammaMove(false);
  }
  delay(DELAY_VALUE);
  motor.moveGammaMotor();

  if (motor.getEndOf_MeassurementProcess()) {
    Serial.print(sensor_Value);
    Serial.print(SEPARATOR);
    Serial.print(NEW_PLANE);
    Serial.print(SEPARATOR);
    Serial.print(END_MEASUREMENT);
    Serial.print(SEPARATOR);
    int isEven = 180 / motor.get_stepCplane();
    if (isEven % 2 != 0) {
      motor.moveForCorrection(-1500);
    } else {
      motor.moveForCorrection(1500);
    }
    motor.set_ref_Position_C_Motor(false);
    motor.set_ref_Position_Gamma_Motor(false);
    device_Rutine = DEVICE_NO_MEASUREMENT;
  }
  delay(DELAY_VALUE);
}

/*
 * "senzor_Inicialisation" - funkcia, zodpovedná, za inicializáciu, a základné nastavenie  
 * fotometrického senzora, 
 */

void senzor_Inicialisation() {
  /*// You can change the gain on the fly, to adapt to brighter/dimmer light situations
    tsl.setGain(TSL2561_GAIN_0X);         // set no gain (for bright situtations)
    //tsl.setGain(TSL2561_GAIN_16X);      // set 16x gain (for dim situations)

    // Changing the integration time gives you a longer time over which to sense light
    // longer timelines are slower, but are good in very low light situtations!
    tsl.setTiming(TSL2561_INTEGRATIONTIME_13MS);  // shortest integration time (bright light)
    //tsl.setTiming(TSL2561_INTEGRATIONTIME_101MS);  // medium integration time (medium light)
    //tsl.setTiming(TSL2561_INTEGRATIONTIME_402MS);  // longest integration time (dim light)*/

  /* You can also manually set the gain or enable auto-gain support */
  // tsl.setGain(TSL2561_GAIN_1X);      /* No gain ... use in bright light to avoid sensor saturation */
  // tsl.setGain(TSL2561_GAIN_16X);     /* 16x gain ... use in low light to boost sensitivity */
  tsl.enableAutoRange(true);            /* Auto-gain ... switches automatically between 1x and 16x */

  /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */
  tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);      /* fast but low resolution */
  // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);  /* medium resolution and speed   */
  // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);  /* 16-bit data but slowest conversions */
}
