#include "storage.h"

#include <EEPROM.h>
#include <esp_attr.h>

// Storage for temperature measurements in RTC memory that survives deep sleep
RTC_DATA_ATTR uint8_t data[rtcStorageSize];
RTC_DATA_ATTR uint16_t dataIndex = 0; // Index of next byte / total byte count
RTC_DATA_ATTR uint16_t rtcDataSum = 0; // Checksum for the data storage
RTC_DATA_ATTR uint16_t numberOfMeasurements = 0; // Current number of measurements (dataIndex / 4)
RTC_DATA_ATTR uint32_t numberOfDiscardedMeasurements = 0; // Previous measurements already deleted

// The time (in seconds since device start) when the current recording started
RTC_DATA_ATTR uint32_t startTimeOfCurrentRecording = 0;

// Keeps the last valid temperatures for each sensor
RTC_DATA_ATTR Temperature lastTemperatures[temperatureSensorCount];
RTC_DATA_ATTR uint32_t lastValidTemperatureTime[temperatureSensorCount];
RTC_DATA_ATTR uint8_t lastMeasurements[temperatureSensorCount] = {0, 0};

void resetLastMeasurements() {
    for (uint8_t index = 0; index < temperatureSensorCount; index += 1) {
        lastTemperatures[index].status == TemperatureStatus::sensorNotFound;
        lastTemperatures[index].value = 0;
        lastValidTemperatureTime[index] = 0;
        lastMeasurements[index] = 0;
    }
}

void storageConfigure(bool isFirstRun) {
    if (isFirstRun) {
        // Ensure that first values are stored
        resetLastMeasurements();
    }
}

uint8_t byteForAbsoluteTemperature(Temperature* temp) {
    switch (temp->status) {
        case TemperatureStatus::sensorError:
        return temperatureSensorFailure;
        case TemperatureStatus::sensorNotFound:
        return temperatureSensorNotAvailable;
        case TemperatureStatus::temperatureIsValid:
        break;
        default:
        return temperatureSensorNotAvailable;
    }
    // Convert to temperature range
    if (temp->value > 87500) {
        return temperatureMaximumValue;
    }
    if (temp->value < -39000) {
        return temperatureMinimumValue;
    }
    // Add 250 to improve rounding
    long converted = (temp->value - (temperatureShiftForStorage) + 250) / 500;
    if (converted < temperatureMinimumValue) {
        return temperatureMinimumValue;
    }
    if (converted > temperatureMaximumValue) {
        return temperatureMaximumValue;
    }
    return converted;
}

void saveByte(uint8_t byte) {
    data[dataIndex] = byte;
    dataIndex += 1;
    rtcDataSum += byte; // Update checksum
}

void saveTemperatures(Temperature* temperatures) {
    if (numberOfMeasurements == 0) {
        // Adjust start time if new measurement is begun
        startTimeOfCurrentRecording = time(NULL);
    }
    if (dataIndex + 4 >= rtcStorageSize) {
        // Don't add any more measurements if storage is full
        return;
    }
    // First: Write timestamp
    // Timestamp is number of measurement intervals since start of recording
    // Add 30 to provide better rounding
    uint16_t timestamp = (time(NULL) - startTimeOfCurrentRecording + 30) / 60;
    uint8_t* ptr = (uint8_t*) &timestamp;
    saveByte(ptr[0]);
    saveByte(ptr[1]);

    // Then: Write bytes for each sensor
    for (uint8_t sensorIndex = 0; sensorIndex < temperatureSensorCount; sensorIndex += 1) {
        Temperature* temp = &temperatures[sensorIndex];
        uint8_t byte = byteForAbsoluteTemperature(temp);
        saveByte(byte);
        lastMeasurements[sensorIndex] = byte;
        if (temp->status == TemperatureStatus::temperatureIsValid) {
            // Only update if temperature is valid
            lastTemperatures[sensorIndex] = *temp;
            lastValidTemperatureTime[sensorIndex] = time(NULL);
        }
    }
    numberOfMeasurements += 1;
}

uint16_t getTotalNumberOfStoredBytes() {
    return dataIndex;
}

uint16_t getNumberOfMeasurements() {
    return numberOfMeasurements;
}

uint32_t getTotalNumberOfMeasurements() {
    return numberOfMeasurements + numberOfDiscardedMeasurements;
}

uint32_t getStartTimeOfCurrentRecording() {
    return startTimeOfCurrentRecording;
}

uint8_t getLastTemperature(uint8_t sensorIndex) {
    return lastMeasurements[sensorIndex];
}

uint16_t getTimeSinceValidTemperature(uint8_t sensorIndex) {
    return min(time(NULL) - lastValidTemperatureTime[sensorIndex], 65535ul);
}

uint16_t getRecordedBytesAtOffset(uint8_t* buffer, uint16_t offset, uint16_t count) {
    if (offset >= dataIndex) {
        return 0;
    }
    uint16_t remainingBytes = dataIndex - offset;
    uint16_t bytesToCopy = min(count, remainingBytes);
    memcpy(buffer, data + offset, bytesToCopy);
    return bytesToCopy;
}

uint16_t getDataChecksum() {
    return rtcDataSum;
}

void discardAllRecordedBytes() {
    dataIndex = 0;
    rtcDataSum = 0;
    numberOfDiscardedMeasurements += numberOfMeasurements;
    numberOfMeasurements = 0;
}