package iDSS.sim;

import java.util.*;
import java.sql.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.text.*;
import java.beans.*;


import iDSS.*;
import iDSS.math.*;
import iDSS.utils.*;
import iDSS.grids.*;
import iDSS.tidalData.*;
import iDSS.beans2.*;





public class SimulationRun implements Runnable {


    private String simulationName;
    private Grid demGrid;
    private LCGrid2 lcGrid2;
    private Hashtable tidalModelHashtable;
    private ClientDataSet slrScenarioDataSet;
    private int simulateUntil,resultsTimeStep;
    private String simulationTimeStepUnits,resultsTimeStepUnits;
    private File saveLocation;
    private boolean viewFloodGrid,saveFloodGrid,viewDDGraph,saveDDGraph;

    private long simulationTimeStep,simulationStartTime;
    private int isostaticMovement;
    private ClientDataSet computedTidalDataSet;

    private int startYear;
    private File fdGridsSavePath,summaryGridsSavePath;

    private Thread simThread;

    PropertyChangeSupport pcs;
    private Vector floodStartVector;






    public SimulationRun() {


        System.out.println("In SimulationRun constructor");

        pcs = new PropertyChangeSupport(this);


    } //public SimulationRun()





    public void addPropertyChangeListener(PropertyChangeListener l) {

        pcs.addPropertyChangeListener(l);

    }//public void addPropertyChangeListener(PropertyChangeListener l)



    public void removePropertyChangeListener(PropertyChangeListener l) {

        pcs.removePropertyChangeListener(l);

    }//public void removePropertyChangeListener(PropertyChangeListener l)





    public void xxSimData(SimulationJBean simulationJBean) {

        setSimulationData(simulationJBean);

    } //public void xxSimData(SimulationJBean simulationJBean)






    public void setSimulationData(SimulationJBean simulationJBean) {

        System.out.println("SimulationRun.setSimulationData()");

        simulationName = simulationJBean.getSimulationName();
        demGrid = simulationJBean.getDem();
        lcGrid2 = simulationJBean.getLcGrid2();

        tidalModelHashtable = simulationJBean.getTidalModelHashtable();

        slrScenarioDataSet = simulationJBean.getSlrDataSet();

        simulateUntil = simulationJBean.getSimulateUntil();

        long aLong = 60000;     //to express simulationTimestep in milliSeconds
        simulationTimeStep = ((long)simulationJBean.getTimestepMinutes())*aLong;

        resultsTimeStep = simulationJBean.getResultsTimestepYears();

        saveLocation = simulationJBean.getSaveLocationDir();

        viewFloodGrid = simulationJBean.getViewFG();
        saveFloodGrid = simulationJBean.getSaveFG();
        viewDDGraph = simulationJBean.getViewDD();
        saveDDGraph = simulationJBean.getSaveDD();
        isostaticMovement = simulationJBean.getIsostaticMovement();
        floodStartVector = simulationJBean.getFloodStartVector();


        System.out.println("Simulation name : " + simulationName);
        System.out.println("DEM grid        : " + demGrid.getName());
        System.out.println("Landcover grid2 : " + lcGrid2.getName());
        System.out.println("# tidal models  : " + tidalModelHashtable.size());
        System.out.println("SLR scenario    : " + slrScenarioDataSet.getSqlString());
        System.out.println("Simulate until  : " + simulateUntil);
        System.out.println("Simulation step : " + simulationTimeStep + " millisec");
        System.out.println("Results step    : " + resultsTimeStep + " year");
        System.out.println("Simulation name : " + simulationName);
        System.out.println("View Flood Grid : " + viewFloodGrid);
        System.out.println("Save Flood Grid : " + saveFloodGrid);
        System.out.println("View Depth Dur  : " + viewDDGraph);
        System.out.println("Save Depth Dur  : " + saveDDGraph);


        fdGridsSavePath = new File(saveLocation,simulationName+"\\fdGrids");

        if(!fdGridsSavePath.exists())
            fdGridsSavePath.mkdirs();

        summaryGridsSavePath = new File(saveLocation,simulationName+"\\summaryGrids");

        if(!summaryGridsSavePath.exists())
            summaryGridsSavePath.mkdirs();

        File lcGrid2File = new File(saveLocation,simulationName+"\\summaryGrids\\landCoverHistory");
        lcGrid2.setFile(lcGrid2File);


    } //public void setSimulationData(SimulationJBean bean)







    public void run() {

        startSimulation();

    }






    private void startSimulation() {

        System.out.println("In SimulationRun startSimulation()");

        //Determine startYear
        startYear = getStartYear();

        //Computer tidal water levels over a year from the tidal models;
        computedTidalDataSet = computeTidalDataSet();

        System.out.println("For the computed tidal dataSet:");
        System.out.println("sqlString   : " + computedTidalDataSet.getSqlString());
        System.out.println("stationName : " + computedTidalDataSet.getStationName());
        System.out.println("timeMin     : " + computedTidalDataSet.getTimeMin());
        System.out.println("timeMax     : " + computedTidalDataSet.getTimeMax());
        System.out.println("valueMin    : " + computedTidalDataSet.getValueMin());
        System.out.println("valueMax    : " + computedTidalDataSet.getValueMax());

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        SimpleDateFormat dateFormatName = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        String decimalPattern = "0.00";
        DecimalFormat decimalFormat = new DecimalFormat(decimalPattern);


        for(int iYear=startYear;iYear<=simulateUntil;iYear++) {

            System.out.print("Present year : " + iYear);

            File yearlyGridFile = new File(summaryGridsSavePath,"SUM"+String.valueOf(iYear));

            Grid2 yearlyGrid = new Grid2(String.valueOf(iYear),demGrid.getUnits(),
                                yearlyGridFile,demGrid.getNRows(),demGrid.getNColumns(),demGrid.getCellSize(),
                                demGrid.getxLLCorner(),demGrid.getyLLCorner());

            Calendar cal = Calendar.getInstance();
            cal.set(iYear,0,1,0,0,0);
            Timestamp time = new Timestamp(cal.getTime().getTime());
            double slr = slrScenarioDataSet.getDataAt(time);

            if(Math.abs(slr-(-9999)) > 1) {

                System.out.println("  slr = " + slr);

                Vector dataColumns = computedTidalDataSet.getColumnsOfDataVector();
                Vector timeColumn = (Vector) dataColumns.elementAt(0);
                Vector valueColumn = (Vector) dataColumns.elementAt(1);
                double floodHeight = 0;
                Timestamp t = null;

                for(int i=0;i<valueColumn.size();i++) {

                    Double d = (Double) valueColumn.elementAt(i);

                    floodHeight = d.doubleValue()+slr/100-(double)isostaticMovement*(double)(iYear-startYear)/1000;

                    t = (Timestamp) timeColumn.elementAt(i);

                    Calendar c = Calendar.getInstance();
                    c.setTime(t);
                    c.set(Calendar.YEAR,iYear);
                    t = new Timestamp(c.getTime().getTime());

                    String name = "FD" + dateFormat.format(t);
                    File fileName = new File(fdGridsSavePath,name);
                    name = "FD:"+dateFormatName.format(t)+":"+decimalFormat.format(floodHeight);

                    setStatus(t);
                    simThread = Thread.currentThread();
                    try {
                        simThread.sleep(1);
                    }
                    catch(InterruptedException intExc) {
                        System.out.println("In SimulationRun.startSimulation(), " + intExc);
                    }

                    double floodSourceX = 0;
                    double floodSourceY = 0;

                    Grid fdGrid = demGrid.createFloodGrid(floodHeight,name,floodStartVector,fileName);
                    fdGrid.setOutFile(fileName);
                    fdGrid.writeGridObject();

                    //temporary line added
                    //System.out.println("SimulationRun.startSimulation()********");
                    //fdGrid.describe();
                    //temporary line added - end

                    yearlyGrid.buildUp(fdGrid,lcGrid2,simulationTimeStep,t);


                } //for(int i=0;i<valueColumn.size();i++)

                if(t != null)
                    yearlyGrid.roundUp(lcGrid2,simulationTimeStep,floodHeight,t);
                else {

                    String message = "SimulationRun.startSimulation(), computedTidalDataSet is empty!";
                    System.out.println(message);

                    Vector messageVector = new Vector();
                    messageVector.add("Error");
                    messageVector.add(message);
                    messageVector.add(new Integer(IDSSAppConstants.errorMessage));

                    pcs.firePropertyChange(IDSSAppConstants.showMessage,null,messageVector);

                    //JOptionPane.showMessageDialog(null,message,"Error",
                                            //JOptionPane.ERROR_MESSAGE);

                } //else

                yearlyGrid.writeGrid2Object();
                //lcGrid2.appendHistory(yearlyGrid,iYear);                     //Blocked to save memory

            } //if(Math.abs(slr-(-9999)) > 1)

        } //for(int iYear=startYear;iYear<=simulateUntil;iYear++)

        //lcGrid2.writeLCGrid2Object();                     //Blocked to save memory
        pcs.firePropertyChange(IDSSAppConstants.notifySimulationEnd,new Boolean(false),new Boolean(true));


    } //private void startSimulation()







    private ClientDataSet computeTidalDataSet() {

        System.out.println("Computing tidalDataSet...");

        Vector columns = new Vector();
        Vector rows = new Vector();

        columns.add("Time");
        columns.add("Computed Tide");


        Calendar cal = Calendar.getInstance();
        cal.set(startYear,0,1,0,0,0);
        simulationStartTime = (cal.getTime().getTime()/1000)*1000;
        Timestamp basisStart = new Timestamp(simulationStartTime);

        cal.set(startYear+1,0,1,0,0,0);
        long simulationEndTime = (cal.getTime().getTime()/1000)*1000;
        Timestamp basisEnd = new Timestamp(simulationEndTime);


        boolean keepGoing = true;

        long time = simulationStartTime;

        int timeStep = 0;


        while(keepGoing) {

            Enumeration modelKeys = tidalModelHashtable.keys();

            double weightedTide = 0;
            double totalWeightage = 0;

            while(modelKeys.hasMoreElements()) {

                String modelName = (String)modelKeys.nextElement();
                TidalModel aTM = (TidalModel) tidalModelHashtable.get(modelName);

                basisStart = aTM.getBasisStart();
                time = basisStart.getTime()+timeStep*simulationTimeStep;
                //Timestamp currentTime = new Timestamp(time);

                basisEnd = aTM.getBasisEnd();

                if (time > basisEnd.getTime()) {
                    keepGoing = false;

                }

                else {

                    double tide = Fourier.computeY((double)time,aTM.getFourierA(),aTM.getFourierB(),aTM.getFourierC(),aTM.getFourierPeriod());

                    Timestamp aTimestamp = new Timestamp(time);
                    //System.out.println("Time, tide : " + aTimestamp.toString() + ", " + tide);

                    tide = tide*aTM.getWeightage();

                    weightedTide = weightedTide + tide;
                    totalWeightage = totalWeightage + aTM.getWeightage();

                } //else


            } //while(modelKeys.hasMoreElements())

            if(keepGoing) {

                weightedTide = weightedTide/totalWeightage;

                Vector currentRow = new Vector();
                Timestamp aTimestamp = new Timestamp(time);

                currentRow.add(aTimestamp);
                currentRow.add(new Double(weightedTide));
                rows.add(currentRow);

                //System.out.println(aTimestamp.toString() + "," + weightedTide);

                timeStep++;

            } //if(keepGoing)


        } //while(keepGoing)


        Enumeration keys = tidalModelHashtable.keys();

        String sqlString = "";
        String stationName = "";

        while(keys.hasMoreElements()) {

            String modelName = (String)keys.nextElement();
            TidalModel aTM = (TidalModel) tidalModelHashtable.get(modelName);
            int wt = aTM.getWeightage();

            sqlString = sqlString+modelName+","+wt+":";
            stationName = stationName+modelName+":";

        } //while(keys.hasMoreElements())

        //ClientDataSet dataSet = new ClientDataSet(sqlString,stationName,start,end,columns,rows);
        ClientDataSet dataSet = new ClientDataSet(sqlString,stationName,basisStart,basisEnd,columns,rows);

        return dataSet;

    } //private ClientDataSet computeTidalDataSet()













    private int getStartYear() {

        System.out.println("In SimulationRun getStartYear()");

        int startYear = 2000;
        Enumeration models = tidalModelHashtable.elements();

        while(models.hasMoreElements()) {

            TidalModel tm = (TidalModel) models.nextElement();
            Timestamp start = tm.getBasisStart();
            Calendar cal = Calendar.getInstance();
            cal.setTime(start);
            int year = cal.get(Calendar.YEAR);

            if (year > startYear) {
                startYear = year;
            } //if (year > startYear)

        } //while(modelKeys.hasMoreElements())

        System.out.println("Start year = : "+ startYear);
        return startYear;

    } //private int getStartYear()









    private void setStatus(Timestamp t) {

        pcs.firePropertyChange(IDSSAppConstants.setSimulationStatus,null,t);

    } //private void setStatus(Timestamp t)





} //public class SimulationRun

