package com.henrypump.poc;

import org.bson.Document;

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

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;
    private String units = null;
    private boolean useTotal;
    private boolean useAverage;
    private long dbId;

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

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


        this.dbId = this.db.tryNewMeasurementConfig(this);

        double lastStored = this.db.getLastStoredMeasurement(this.dbId);
        if (lastStored != Double.MIN_VALUE) {
            this.lastSentValue = this.db.getLastStoredMeasurement(this.dbId);
        }
//        try {
//            Date lastStoredTimestamp = (Date) lastStored.get("dateStored");
//            ZonedDateTime timestamp = ZonedDateTime.ofInstant(lastStoredTimestamp.toInstant(),
//                    ZoneId.systemDefault());
//            if (isToday(timestamp)){
//                this.average = lastStored.getDouble("averageValue");
//                this.total = lastStored.getDouble("totalValue");
////                this.lastSentValue = lastStored.getDouble("currentValue");
//                this.dailyMax = lastStored.getDouble("maxValue");
//                this.dailyMin = lastStored.getDouble("minValue");
//                this.sendDelta = lastStored.getDouble("storeDelta");
//                this.sendTimeDelta = lastStored.getLong("storeTime");
//                this.units = lastStored.getString("units");
////                this.lastSentTimestamp = timestamp.toEpochSecond();
//                Document values = (Document) lastStored.get("values");
//                Object[] hours = values.keySet().toArray();
//                String maxHour = (String) hours[hours.length - 1];
//                Document maxHourDoc = (Document) values.get(maxHour);
//                Object[] minutes = maxHourDoc.keySet().toArray();
//                String maxMinute = (String) minutes[minutes.length - 1];
//                this.lastSentValue = maxHourDoc.getDouble(maxMinute);
//                this.numMeasurements = lastStored.getLong("numMeasurements");
//
//
//                System.out.println("Using stored value from " + ZonedDateTime.of(timestamp.getYear(),
//                        timestamp.getMonthValue(), timestamp.getDayOfMonth(), Integer.parseInt(maxHour),
//                        Integer.parseInt(maxMinute), 0, 0, timestamp.getZone()).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);
//        }

    }


    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;
    }

    public double getSendDelta() {
        return sendDelta;
    }

    public void setSendDelta(double sendDelta) {
        this.sendDelta = sendDelta;
    }

    public long getSendTimeDelta() {
        return sendTimeDelta;
    }

    public void setSendTimeDelta(long sendTimeDelta) {
        this.sendTimeDelta = sendTimeDelta;
    }

    public String getUnits() {
        return units;
    }

    public void setUnits(String units) {
        this.units = units;
    }

    public boolean isUseTotal() {
        return useTotal;
    }

    public void setUseTotal(boolean useTotal) {
        this.useTotal = useTotal;
    }

    public boolean isUseAverage() {
        return useAverage;
    }

    public void setUseAverage(boolean useAverage) {
        this.useAverage = useAverage;
    }

    public long getDbId() {
        return dbId;
    }

    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.newMeasurementValue(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;
    }
}
