/* MHZ library

    By Tobias Schürg
*/

#include "MHZ.h"

const int MHZ14A = 14;
const int MHZ19B = 19;

const int MHZ14A_RESPONSE_TIME = 60;
const int MHZ19B_RESPONSE_TIME = 120;

const int STATUS_NO_RESPONSE = -2;
const int STATUS_CHECKSUM_MISMATCH = -3;
const int STATUS_INCOMPLETE = -4;
const int STATUS_NOT_READY = -5;

unsigned long lastRequest = 0;

boolean ready = false;

MHZ::MHZ(uint8_t rxpin, uint8_t txpin, uint8_t pwmpin, uint8_t type)
    : co2Serial(rxpin, txpin) {
  _rxpin = rxpin;
  _txpin = txpin;
  _pwmpin = pwmpin;
  _type = type;
  last_ppm = 0;

  co2Serial.begin(9600);
}

/**
 * Enables or disables the debug mode (more logging).
 */
void MHZ::setDebug(boolean enable) {
  debug = enable;
  if (debug) {
    Serial.println("MHZ: debug mode ENABLED");
  } else {
    Serial.println("MHZ: debug mode DISABLED");
  }
}

boolean MHZ::isPreHeating() {
	return false;
	if (ready) return false;
  if (_type == MHZ14A) {
	ready = (millis() > (2 * 60 * 1000l));
    return !ready;
  } else if (_type == MHZ19B) {
	ready = (millis() > (2 * 60 * 1000l));
    return !ready;
  } else {
    Serial.println("MHZ::isPreHeating() => UNKNOWN SENSOR");
    return false;
  }
}

boolean MHZ::isReady() {
  if (isPreHeating()) return false;
  unsigned long cms = millis();
  if (lastRequest > cms)
	  if (lastRequest - cms > 24L*60L*60L*1000L)
	  	lastRequest = cms;
  if (_type == MHZ14A)
    return lastRequest < cms - MHZ14A_RESPONSE_TIME;
  else if (_type == MHZ19B)
    return lastRequest < cms - MHZ19B_RESPONSE_TIME;
  else {
    Serial.print("MHZ::isReady() => UNKNOWN SENSOR \"");
    Serial.print(_type);
    Serial.println("\"");
    return true;
  }
}

void MHZ::sendCO2UARTRequest() {
  if (!isReady()) return;
  if (debug) Serial.println("-- read CO2 uart ---");
  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};

  if (debug) Serial.print("  >> Sending CO2 request");
  co2Serial.write(cmd, 9);  // request PPM CO2
  lastRequest = millis();


 /* int waited = 0;
  while (co2Serial.available() == 0) {
    if (debug) Serial.print(".");
    delay(100);  // wait a short moment to avoid false reading
    if (waited++ > 10) {
      if (debug) Serial.println("No response after 10 seconds");*/
      co2Serial.flush();
      /*return STATUS_NO_RESPONSE;
    }
  }
  if (debug) Serial.println();*/
}

int MHZ::readCO2UART() {
  // clear the buffer
  byte response[9];  // for answer
  memset(response, 0, 9);
  // The serial stream can get out of sync. The response starts with 0xff, try
  // to resync.
  // TODO: I think this might be wrong any only happens during initialization?
  boolean skip = false;
  while ((co2Serial.available() > 0) && ((unsigned char)co2Serial.peek() != 0xFF)) {
    if (!skip) {
		if (debug) Serial.print("MHZ: - skipping unexpected readings:");
		skip = true;
    }
    if (debug) {
		Serial.print(" ");
		Serial.print(co2Serial.peek(), HEX);
    }
	co2Serial.read();
  }
  if (skip && debug) Serial.println();

  if (co2Serial.available() > 0) {
    int count = co2Serial.readBytes(response, 9);
    if (count < 9) {
      co2Serial.flush();
      return STATUS_INCOMPLETE;
    }
  } else {
    co2Serial.flush();
    return STATUS_INCOMPLETE;
  }

  if (debug) {
    // print out the response in hexa
    Serial.print("  << ");
    for (int i = 0; i < 9; i++) {
      Serial.print(response[i], HEX);
      Serial.print("  ");
    }
    Serial.println("");
  }

  // checksum
  byte check = getCheckSum(response);
  if (response[8] != check) {
	if (debug) {
		Serial.println("MHZ: Checksum not OK!");
		Serial.print("MHZ: Received: ");
		Serial.println(response[8], HEX);
		Serial.print("MHZ: Should be: ");
		Serial.println(check, HEX);
	}
    temperature = STATUS_CHECKSUM_MISMATCH;
    co2Serial.flush();
    return STATUS_CHECKSUM_MISMATCH;
  }

  last_ppm = 256 * (int)response[2] + response[3];

  temperature = response[4] - 44;  // - 40;

  byte status = response[5];
  if (debug) {
    Serial.print(" # PPM UART: ");
    Serial.println(last_ppm);
    Serial.print(" # Temperature? ");
    Serial.println(temperature);
  }

  // Is always 0 for version 14a  and 19b
  // Version 19a?: status != 0x40
	if (debug) {
		if (status != 0) {
			Serial.print(" ! Status maybe not OK ! ");
			Serial.println(status, HEX);
		} else {
			Serial.print(" Status  OK: ");
			Serial.println(status, HEX);
		}
	}

  co2Serial.flush();
  return last_ppm;
}

uint8_t MHZ::getLastTemperature() {
  if (!isReady()) return STATUS_NOT_READY;
  return temperature;
}

byte MHZ::getCheckSum(byte* packet) {
  if (debug) Serial.println("  getCheckSum()");
  byte i;
  unsigned char checksum = 0;
  for (i = 1; i < 8; i++) {
    checksum += packet[i];
  }
  checksum = 0xff - checksum;
  checksum += 1;
  return checksum;
}

int MHZ::readCO2PWM() {
  // if (!isReady()) return STATUS_NOT_READY; not needed?
  if (debug) Serial.print("-- reading CO2 from pwm ");
  unsigned long th, tl, ppm_pwm = 0;
  do {
    if (debug) Serial.print(".");
    th = pulseIn(_pwmpin, HIGH, 1004000) / 1000;
    tl = 1004 - th;
    ppm_pwm = 5000 * (th - 2) / (th + tl - 4);
  } while (th == 0);
  if (debug) {
    Serial.print("\n # PPM PWM: ");
    Serial.println(ppm_pwm);
  }
  return ppm_pwm;
}
