package com.henrypump.poc;

import org.bson.Document;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

import static java.lang.Math.abs;

/**
 * Created by patrickjmcd on 2/3/17.
 */
public class Measurement {
    private String tagName;
    private double currentValue;
    private double lastValue;
    private double average;
    private double total;
    private double dailyMax;
    private double dailyMin;
    private double[] totalHistory = new double[30];
    private double[] averageHistory = new double[30];
    private long numMeasurements;
    private double lastSentValue;
    private double sendDelta;
    private Database db;
    private long lastSentTimestamp;
    private long sendTimeDelta;

    Measurement(String tagName, Database db, double sendDelta, long sendTimeDelta){
        this.tagName = tagName;
        average = 0;
        total = 0;
        numMeasurements = 0;
        dailyMax = Double.MIN_VALUE;
        dailyMin = Double.MAX_VALUE;

        // Database code
        this.db = db;
        this.sendDelta = sendDelta;
        this.lastSentValue = 0.0;
        this.sendTimeDelta = sendTimeDelta;

        Document lastStored =  this.db.getLastStoredMeasurement(this.tagName);
        try {
            Date lastStoredTimestamp = (Date) lastStored.get("timestamp");
            ZonedDateTime timestamp = ZonedDateTime.ofInstant(lastStoredTimestamp.toInstant(),
                    ZoneId.systemDefault());
            if (isToday(timestamp)){
                this.average = lastStored.getDouble("dailyAverage");
                this.total = lastStored.getDouble("dailyTotal");
                this.lastSentValue = lastStored.getDouble("currentValue");
                this.dailyMax = lastStored.getDouble("maxDailyValue");
                this.dailyMin = lastStored.getDouble("minDailyValue");
                this.lastSentTimestamp = timestamp.toEpochSecond();
                this.numMeasurements = lastStored.getLong("numMeasurements");
                System.out.println("Using stored value from " + timestamp.toString() + " for " + this.tagName);
            } else {
                System.out.println("Cannot use stored value from " + timestamp.toString() + " for " + this.tagName);
            }
        } catch (NullPointerException e){
            System.out.println("There was no previous measurement in the database for " + this.tagName);
        }

    }

//    Measurement(String tagName, boolean storeInDatabase){
//        this.tagName = tagName;
//        average = 0;
//        total = 0;
//        numMeasurements = 0;
//        dailyMax = Double.MIN_VALUE;
//        dailyMin = Double.MAX_VALUE;
//
//        // Database code
//        this.storeInDatabase = storeInDatabase;
//    }

    public double getCurrentValue() {
        return currentValue;
    }

    public double getLastValue() {
        return lastValue;
    }

    public double getAverage() {
        return average;
    }

    public double getTotal() {
        return total;
    }

    public long getNumMeasurements() {
        return numMeasurements;
    }

    public String getTagName() {
        return tagName;
    }

    public double getDailyMax() {
        return dailyMax;
    }

    public double getDailyMin() {
        return dailyMin;
    }

    private static boolean isToday(ZonedDateTime inpZDT){
        ZonedDateTime now = ZonedDateTime.now();
        if (now.toLocalDate().equals(inpZDT.toLocalDate())){
            return true;
        }
        return false;
    };

    void update(double value)
    {
        long currentTimestamp = ZonedDateTime.now().toEpochSecond();
        lastValue = currentValue;
        currentValue = value;

        numMeasurements = numMeasurements + 1;
        average = average * (((float)numMeasurements - 1)/(float)numMeasurements) + (currentValue / (float)numMeasurements);
        total = total + value;

        dailyMax = Math.max(dailyMax, currentValue);
        dailyMin = Math.min(dailyMin, currentValue);

        if(abs(currentValue - lastSentValue) > sendDelta || (currentTimestamp - lastSentTimestamp) > (sendTimeDelta)){
            long l = db.newMeasurement(this);
            lastSentValue = currentValue;
            lastSentTimestamp = currentTimestamp;
        }
    }

    void endOfDay(){
        db.newDailyTotal(this);
        System.arraycopy(totalHistory, 0, totalHistory, 1, totalHistory.length-1);
        System.arraycopy(averageHistory, 0, averageHistory, 1, averageHistory.length-1);

        totalHistory[0] = total;
        averageHistory[0] = average;

        total = 0;
        average = 0;
        dailyMax = Double.MIN_VALUE;
        dailyMin = Double.MAX_VALUE;
        numMeasurements = 0;
    }
}
