#include "Battery.h"

void Battery::init() {
    powerCheck.setup();
}

void Battery::process(time_t now) {
    if ((now - lastBatteryStateSentTimeStamp) > batteryTimeInterval) {
        stateOfCharge = fuel.getSoC();
        Particle.publish("charge", String(fuel.getSoC()), 3600, PRIVATE);
        Particle.publish("vCell", String(fuel.getVCell()), 3600, PRIVATE);
        lastBatteryStateSentTimeStamp = now;
    }
    Particle.variable("charge", stateOfCharge);
 
    if (millis() - lastCheck > 2000) {
		lastCheck = millis();

		char buffer[256];
		snprintf(buffer, sizeof(buffer),
            "{\"hasPower\":%d, \"hasBattery\":%d}",
			// "{\"hasPower\":%d, \"hasBattery\":%d, \"isCharging\":%d}",
			powerCheck.getHasPower(),
            powerCheck.getHasBattery()
        );

		if (strcmp(lastStatus, buffer) != 0) {
			strcpy(lastStatus, buffer);
			Particle.publish("battery", buffer, PRIVATE);
		}
	}
}


Battery::PowerCheck::PowerCheck() {
}

Battery::PowerCheck::~PowerCheck() {
}

void Battery::PowerCheck::setup() {
	// This can't be part of the constructor because it's initialized too early.
	// Call this from setup() instead.

	// BATT_INT_PC13
	attachInterrupt(LOW_BAT_UC, &PowerCheck::interruptHandler, this, FALLING);
}

bool Battery::PowerCheck::getHasPower() {
	// Bit 2 (mask 0x4) == PG_STAT. If non-zero, power is good
	// This means we're powered off USB or VIN, so we don't know for sure if there's a battery
	byte systemStatus = pmic.getSystemStatus();
	return ((systemStatus & 0x04) != 0);
}

/**
 * Returns true if the Electron has a battery.
 */
bool Battery::PowerCheck::getHasBattery() {
	if (millis() - lastChange < 100) {
		// When there is no battery, the charge status goes rapidly between fast charge and
		// charge done, about 30 times per second.

		// Normally this case means we have no battery, but return hasBattery instead to take
		// care of the case that the state changed because the battery just became charged
		// or the charger was plugged in or unplugged, etc.
		return hasBattery;
	}
	else {
		// It's been more than a 100 ms. since the charge status changed, assume that there is
		// a battery
		return true;
	}
}


/**
 * Returns true if the Electron is currently charging (red light on)
 */
bool Battery::PowerCheck::getIsCharging() {
	if (getHasBattery()) {
		byte systemStatus = pmic.getSystemStatus();

		// Bit 5 CHRG_STAT[1] R
		// Bit 4 CHRG_STAT[0] R
		// 00 – Not Charging, 01 – Pre-charge (<VBATLOWV), 10 – Fast Charging, 11 – Charge Termination Done
		byte chrgStat = (systemStatus >> 4) & 0x3;

		// Return true if battery is charging if in pre-charge or fast charge mode
		return (chrgStat == 1 || chrgStat == 2);
	}
	else {
		// Does not have a battery, can't be charging.
		// Don't just return the charge status because it's rapidly switching
		// between charging and done when there is no battery.
		return false;
	}
}

void Battery::PowerCheck::interruptHandler() {
	if (millis() - lastChange < 100) {
		// We very recently had a change; assume there is no battey and we're rapidly switching
		// between fast charge and charge done
		hasBattery = false;
	}
	else {
		// Note: It's quite possible that hasBattery will be false when there is a battery; the logic
		// in getHasBattery() takes this into account by checking lastChange as well.
		hasBattery = true;
	}
	lastChange = millis();
}
