package com.henrypump.poc;

/**
 * Created by patrickjmcd on 2/2/17.
 */

import com.mongodb.*;

import com.mongodb.client.AggregateIterable;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Accumulators;
import com.mongodb.client.model.Aggregates;

import com.mongodb.client.model.Sorts;
import org.bson.Document;

import java.time.ZonedDateTime;

import com.mongodb.client.MongoCursor;
import static com.mongodb.client.model.Filters.*;

import javax.print.Doc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
public class Database {
    private String pocDatabase = "poc";
    private MongoClient mongoClient;
    private MongoDatabase database;

    Database(){
        mongoClient = new MongoClient();
        database = mongoClient.getDatabase(pocDatabase);
    }

    Database(String dbHostname){
        mongoClient = new MongoClient(dbHostname);
        database = mongoClient.getDatabase(pocDatabase);
    }

    public long getLastStrokeNum(){

        long lastStroke = 0;

        MongoCollection<Document> collection = database.getCollection("cards");
        MongoCursor<Document> cursor = collection.aggregate(
                Arrays.asList(
                        Aggregates.group("strokeNumber", Accumulators.last("lastStroke", "$strokeNumber"))
                )
        ).iterator();

        try {
            while (cursor.hasNext()) {
                long docStroke = cursor.next().getLong("lastStroke");
                if (docStroke  > lastStroke) { lastStroke = docStroke; }
            }
        } finally {
            cursor.close();
        }
        System.out.println("Found last stroke to be " + lastStroke);
        return lastStroke;
    }

    public long newCard(Card inpCard){
        MongoCollection<Document> collection = database.getCollection("cards");
        List<Double> s_p = new ArrayList<Double>();
        List<Double> s_l = new ArrayList<Double>();
        List<Double> d_p = new ArrayList<Double>();
        List<Double> d_l = new ArrayList<Double>();
        int numPointsScan = inpCard.getNumPointsUsed();
        double[] sp = inpCard.getSurfacePosition();
        double[] sl = inpCard.getSurfaceLoad();
        double[] dp = inpCard.getDownholePosition();
        double[] dl = inpCard.getDownholeLoad();
        for(int i = 0; i < numPointsScan; i++ ) {
            s_p.add(sp[i]);
            s_l.add(sl[i]);
            d_p.add(dp[i]);
            d_l.add(dl[i]);
        }

        Document doc = new Document("strokeNumber", inpCard.getStrokeNumber())
                .append("surface_position", s_p)
                .append("surface_load", s_l)
                .append("downhole_position", d_p)
                .append("downhole_load", d_l)
                .append("timestamp", Date.from(ZonedDateTime.now().toInstant()));
        collection.insertOne(doc);
        System.out.println("Stored stroke number " + inpCard.getStrokeNumber() + " as " + doc.getObjectId("_id"));
        return collection.count();
    }
    public void close(){
        mongoClient.close();
    }

    public long newMeasurement(Measurement inpMeasurement){
        MongoCollection<Document> collection = database.getCollection("wellData");
        Document doc = new Document("tagname", inpMeasurement.getTagName())
                .append("currentValue", inpMeasurement.getCurrentValue())
                .append("maxDailyValue", inpMeasurement.getDailyMax())
                .append("minDailyValue", inpMeasurement.getDailyMin())
                .append("dailyAverage", inpMeasurement.getAverage())
                .append("dailyTotal", inpMeasurement.getTotal())
                .append("numMeasurements", inpMeasurement.getNumMeasurements())
                .append("timestamp", Date.from(ZonedDateTime.now().toInstant()));
        collection.insertOne(doc);
        System.out.println("Stored " + inpMeasurement.getCurrentValue() + " for " + inpMeasurement.getTagName());
        return collection.count();
    }

    public Document getLastStoredMeasurement(String tagName){
        MongoCollection<Document> collection = database.getCollection("wellData");
        MongoCursor<Document> cursor = collection.find(eq("tagname", tagName)).sort(Sorts.descending("timestamp")).limit(1).iterator();
        Document lastStoredDoc = new Document();
        try {
            while (cursor.hasNext()) {
                lastStoredDoc = cursor.next();
            }
        } finally {
            cursor.close();
        }
        return lastStoredDoc;
    }

    public long newDailyTotal(Measurement inpMeasurement){
        MongoCollection<Document> collection = database.getCollection("gaugeOff");
        Document doc = new Document("tagname", inpMeasurement.getTagName())
                .append("currentValue", inpMeasurement.getCurrentValue())
                .append("maxDailyValue", inpMeasurement.getDailyMax())
                .append("minDailyValue", inpMeasurement.getDailyMin())
                .append("dailyAverage", inpMeasurement.getAverage())
                .append("dailyTotal", inpMeasurement.getTotal())
                .append("timestamp", Date.from(ZonedDateTime.now().toInstant()));
        collection.insertOne(doc);
        return collection.count();

    }

    public double getPreviousDailyTotal(ZonedDateTime inpDateTime){
        String isoInpDateTime = inpDateTime.toString();
        MongoCollection<Document> wellTestCollection = database.getCollection("gaugeOff");
        MongoCursor<Document> cursor = wellTestCollection.find(and(eq("tagname", "Fluid Produced"), lte("timestamp", isoInpDateTime)))
                .sort(Sorts.descending("timestamp")).limit(1).iterator();
        double lastTotal = -1.0;
        try {
            while (cursor.hasNext()) {
                lastTotal = cursor.next().getDouble("dailyTotal");
            }
        } finally {
            cursor.close();
        }
        return lastTotal;
    };

    public long newWellTest(WellTest inp){
        MongoCollection<Document> collection = database.getCollection("wellTests");

        Document doc = new Document("testStartTime", Date.from(inp.getTestStart().toInstant()))
                .append("testHours", inp.getTestHours())
                .append("testTotalBBL", inp.getTotalFluidBBL())
                .append("testOilBBL", inp.getTestOilBBL())
                .append("testWaterBBL", inp.getTestWaterBBL())
                .append("testGasMCF", inp.getTestGasMCF())
                .append("kFactor", inp.getkFactor())
                .append("oilRatio", inp.getOilRatio())
                .append("waterRatio", inp.getWaterRatio())
                .append("gasMCFRatio", inp.getGasMCFRatio());
        collection.insertOne(doc);
        return collection.count();
    }

    public Document getPreviousWellTest(ZonedDateTime inpDateTime){
        MongoCollection<Document> wellTestCollection = database.getCollection("wellTests");
        MongoCursor<Document> cursor = wellTestCollection.find(lte("testStartTime", Date.from(inpDateTime.toInstant())))
                .sort(Sorts.descending("testStartTime")).limit(1).iterator();
        Document lastTest = new Document("kFactor", (Double) 1.0);
        try {
            while (cursor.hasNext()) {
                lastTest = cursor.next();
            }
        } finally {
            cursor.close();
        }
        return lastTest;
    };

    public double getLatestKFactor(){
        MongoCollection<Document> wellTestCollection = database.getCollection("wellTests");
        MongoCursor<Document> cursor = wellTestCollection.find().sort(Sorts.descending("testStartTime")).limit(1).iterator();
        double kFactor = 1.0;
        try {
            while (cursor.hasNext()) {
                kFactor = cursor.next().getDouble("kFactor");
            }
        } finally {
            cursor.close();
        }
        return kFactor;
    }

    public long newFluidShot(FluidShot inp){
        String df = ZonedDateTime.now().toString();
        MongoCollection<Document> collection = database.getCollection("fluidShots");
        Document doc = new Document("timestamp", Date.from(ZonedDateTime.now().toInstant()))
                .append("fluidLevel", inp.getFluidLevel())
                .append("pumpIntakePressure", inp.getPumpIntakePressure())
                .append("frictionEstimate", inp.getFrictionEstimate());
        collection.insertOne(doc);
        return collection.count();
    }

    public double getLatestFrictionEstimate(){
        MongoCollection<Document> wellTestCollection = database.getCollection("fluidShots");
        MongoCursor<Document> cursor = wellTestCollection.find().sort(Sorts.descending("timestamp")).limit(1).iterator();
        double frictionEstimate = -1;
        try {
            while (cursor.hasNext()) {
                frictionEstimate = cursor.next().getDouble("frictionEstimate");
            }
        } finally {
            cursor.close();
        }
        return frictionEstimate;
    }

    public long newRunStatus(String runStatus, String initiator){
        MongoCollection<Document> collection = database.getCollection("runStatus");

        Document doc = new Document("status", runStatus)
                .append("initiator", initiator)
                .append("timesstamp", Date.from(ZonedDateTime.now().toInstant()));
        collection.insertOne(doc);
        return collection.count();
    }
}
