package iDSS.grids;


import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.sql.Timestamp;


import iDSS.sm.*;
import iDSS.utils.*;



public class Grid2 implements GenericGrid, Serializable {

    private String name;

    private int units;
    private final int UNKNOWN = 0;
    private final int FT = 1;
    private final int IN = 2;
    private final int M = 3;
    private final int MM = 4;

    private double cellSize;
    private int nRows,nColumns;
    private double xLLCorner,yLLCorner;
    private double xURCorner,yURCorner;
    private double xLLC,yLLC,xURC,yURC;
    private String fileName = "";
    private File file;
    private boolean gridReadDone = false;
    private Vector z;


    public Grid2(String title, int gridUnit, File aFile,int rows,int columns,double cell,
                double xLL,double yLL) {

        System.out.println("Creating grid2 " + title);
        System.out.println("nRows/nColumns : " + nRows + "/" + nColumns);

        name = title;
        if(aFile != null) {
            file = aFile;
            fileName = aFile.toString();
        }

        if ((gridUnit > 0) && (gridUnit < 5))
            units = gridUnit;
        else
            units = 0;


        nRows = rows;
        nColumns = columns;
        cellSize = cell;

        xLLCorner = xLL;
        yLLCorner = yLL;

        xURCorner = (nColumns-1) * cellSize;
        yURCorner = (nRows-1) * cellSize;


        xLLC = xLLCorner+cellSize/2;
        yLLC = yLLCorner+cellSize/2;
        xURC = xLLC+(nColumns-1)*cellSize;
        yURC = yLLC+(nRows-1)*cellSize;

        z = new Vector();

        for(int i=0;i<nRows;i++) {

            Vector currentRow = new Vector();

            for(int j=0;j<nColumns;j++) {
                currentRow.add(new GridCell());
            } //for(int j=0;j<nColumns;j++)

            z.add(currentRow);

        } //for (int i=0;i<nRows;i++)

        System.out.println("nRows/nColumns : " + nRows + "/" + nColumns);

    } //public Grid2(...)




    public String getName() {

        return name;

    } //public String getName()




    public long getTotalFloodingTime(double x,double y) {

        long value = -9999;
        Vector v = getRowsColsAndDists(x,y);

        if(v != null) {

            Vector ints = (Vector)v.elementAt(0);
            Vector doubles = (Vector)v.elementAt(1);

            int row1 = Integer.parseInt(ints.elementAt(0).toString());
            int row2 = Integer.parseInt(ints.elementAt(1).toString());
            int col1 = Integer.parseInt(ints.elementAt(2).toString());
            int col2 = Integer.parseInt(ints.elementAt(3).toString());

            double d11 = Double.parseDouble(doubles.elementAt(0).toString());
            double d12 = Double.parseDouble(doubles.elementAt(1).toString());
            double d21 = Double.parseDouble(doubles.elementAt(2).toString());
            double d22 = Double.parseDouble(doubles.elementAt(3).toString());

            double denom = 1/d11 + 1/d21 + 1/d12 + 1/d22;

            GridCell cell11 = getGridCell(col1,row1);
            GridCell cell12 = getGridCell(col1,row2);
            GridCell cell21 = getGridCell(col2,row1);
            GridCell cell22 = getGridCell(col2,row2);

            long v11 = cell11.getTotalFloodingTime();
            long v12 = cell12.getTotalFloodingTime();
            long v21 = cell21.getTotalFloodingTime();
            long v22 = cell22.getTotalFloodingTime();

            value = (new Double(((double)v11/d11 + (double)v12/d12 + (double)v21/d21 + (double)v22/d22)/denom)).longValue();

        } //if(v != null)

        return value;

    } //public long getTotalFloodingTime(double x,double y)







    public GridCell getGridCell(int i,int j) {

        GridCell gridCell = null;

        //if ((i>=0) && (i<nColumns) && (j>=0) && (i<nRows)) {
        if ((i>=0) && (i<nColumns) && (j>=0) && (j<nRows)) {

            Vector vector = (Vector) z.elementAt(j);
            gridCell = (GridCell)vector.elementAt(i);

        } //if ((i>=0) && (i<=nColumns) && (j>=0) && (i<=nRows))

        return gridCell;

    } //private GridCell getValue(int i,int j)







    private Vector getRowsColsAndDists(double x,double y) {

        Vector v = null;

        if ((x>=xLLC) && (x<=xURC) && (y>=yLLC) && (y<=yURC)) {

            v = new Vector();

            double col = (x-xLLC)*((double)nColumns-1)/(xURC-xLLC);
            double row = (nRows-1)-(y-yLLC)*((double)nRows-1)/(yURC-yLLC);

            int row1 = (new Double(Math.floor(row))).intValue();
            int row2 = (new Double(Math.ceil(row))).intValue();

            int col1 = (new Double(Math.floor(col))).intValue();
            int col2 = (new Double(Math.ceil(col))).intValue();

            double x1 = xLLC+(double)col1*cellSize;
            double x2 = xLLC+(double)col2*cellSize;
            double y1 = yURC-(double)row1*cellSize;
            double y2 = yURC-(double)row2*cellSize;

            double d11 = Math.sqrt(Math.pow((x-x1),2)+Math.pow((y-y1),2));
            double d12 = Math.sqrt(Math.pow((x-x1),2)+Math.pow((y-y2),2));
            double d21 = Math.sqrt(Math.pow((x-x2),2)+Math.pow((y-y1),2));
            double d22 = Math.sqrt(Math.pow((x-x2),2)+Math.pow((y-y2),2));

            Vector intValues = new Vector();
            intValues.add(new Integer(row1));
            intValues.add(new Integer(row2));
            intValues.add(new Integer(col1));
            intValues.add(new Integer(col2));

            Vector doubleValues = new Vector();
            doubleValues.add(new Double(d11));
            doubleValues.add(new Double(d12));
            doubleValues.add(new Double(d21));
            doubleValues.add(new Double(d22));

            v.add(intValues);
            v.add(doubleValues);

        } //if ((x>=xLLC) && (x<=xURC) && (y>=yLLC) && (y<=yURC))

        return v;

    } //private Vector getRowColAndDists(double x,double y)





    private void setCellAt(int col,int row,GridCell c) {

        Vector oneRow = (Vector) z.elementAt(row);
        oneRow.remove(col);
        oneRow.insertElementAt(c,col);
        z.remove(row);
        z.insertElementAt(oneRow,row);

    } //private void setCellAt(int col,int row,GridCell c)









    public void writeGrid2Object() {

        try {
            if(file.exists()) {

                int option = JOptionPane.showConfirmDialog(null,
                                    "File already exists, overwrite?",
                                    "Confirm file overwrite",
                                    JOptionPane.YES_NO_CANCEL_OPTION,
                                    JOptionPane.QUESTION_MESSAGE);

                switch(option) {

                    case JOptionPane.YES_OPTION:
                        file.delete();
                        break;

                    case JOptionPane.NO_OPTION:

                        file = FileNameDialog.getOutputFileName("Pick output file",null,null);
                        break;

                    case JOptionPane.CANCEL_OPTION:

                        file = null;
                        break;

                    default:
                        break;

                } //switch(option)

            }//if(outFile.exists())

            if(file.createNewFile()) {
                FileOutputStream f = new FileOutputStream(file);
                ObjectOutputStream ooStream = new ObjectOutputStream(f);
                ooStream.writeObject(this);
                ooStream.flush();
                f.close();
            }
            else
                System.out.println("Failed to create file " + file.toString());

        } //try

        catch (FileNotFoundException fnfe) {
            System.out.print("In Grid2.writeGridObject()");
            System.out.println("File not found : " + fnfe);
        }

        catch (IOException ioe) {
            System.out.print("In Grid2.writeGridObject()");
            System.out.println("IOException : " + ioe);
        }


    } //public void writeGrid2Object(ObjectOutputStream ooStream)










    public static Grid2 readGrid2Object(File aFile) {

        Grid2 aGrid = new Grid2("no title",0,null,0,0,0,0,0);

        try {
            if(aFile.exists()) {

                FileInputStream f = new FileInputStream(aFile);
                ObjectInputStream oiStream = new ObjectInputStream(f);
                aGrid = (Grid2)oiStream.readObject();
                f.close();

                aGrid.describe();

            }//if(!inFile.exists())

            else {
                System.out.println("Input file " + aFile.toString() + " doesn't exist");
            }

        } //try

        catch (FileNotFoundException fnfe) {
            System.out.print("In Grid2.readGridObject()");
            System.out.println("File not found : " + fnfe);
        }

        catch (IOException ioe) {
            System.out.print("In Grid2.readGridObject()");
            System.out.println("IOException : " + ioe);
        }

        catch(ClassNotFoundException cnfe) {
            System.out.print("In Grid2.readGridObject()");
            System.out.println("ClassNotFoundException : " + cnfe);
        }

        catch(ClassCastException cce) {
            System.out.println("ClassCastException : " + cce);
            return null;
        }


        aGrid.gridIsRead(true);
        return aGrid;

    } //public static void readGrid2Object()





    public boolean isGridRead() {

        return gridReadDone;

    } //public boolean isGridRead()



    private void gridIsRead(boolean b) {

        gridReadDone = b;

    } //private void gridIsRead(boolean b)





    public void describe() {

        System.out.println("Describing grid");

        System.out.println("\nName        = " + name +
                           "\nnRows       = " + nRows +
                           "\nnColumns    = " + nColumns +
                           "\nxLLCorner   = " + xLLCorner +
                           "\nyLLCorner   = " + yLLCorner +
                           "\ncellSize    = " + cellSize);

    } //public void describe()





    public void buildUp(Grid floodGrid,LCGrid2 lcGrid2,long simTS,Timestamp currentTime) {

        //System.out.println("Grid2.buildUp(), currentTime : " + currentTime.toString());

        for(int i=0;i<nRows;i++) {
            Vector currentRow = (Vector) z.elementAt(i);

            for(int j=0;j<nColumns;j++) {

                GridCell gridCell = (GridCell) currentRow.elementAt(j);
                double floodDepth = floodGrid.getValue(j,i);
                SurvivalModel sm = lcGrid2.getSurvivalModel(j,i);
                gridCell.buildUP(floodDepth,simTS,currentTime,sm);

                //System.out.print("Grid2.buildUp(), col,row :" + j + "," + i + ": " + gridCell.getStatus() + ", FD : " + floodDepth + ", ");

            } //for(int j=0;j<nColumns;j++)

        } //for(int i=0;i<nRows;i++)

    } //buildUp()




    public void roundUp(LCGrid2 lcGrid2,long simTS,double floodDepth,Timestamp currentTime) {

        //System.out.println("Grid2.roundUp");

        for(int i=0;i<nRows;i++) {
            Vector currentRow = (Vector) z.elementAt(i);

            for(int j=0;j<nColumns;j++) {

                GridCell gridCell = (GridCell) currentRow.elementAt(j);

                //System.out.print("col,row :" + j + "," + i + ": " + gridCell.getStatus() + ", ");

                SurvivalModel sm = lcGrid2.getSurvivalModel(j,i);
                gridCell.roundUp(sm,simTS,floodDepth,currentTime);

            } //for(int j=0;j<nColumns;j++)

        } //for(int i=0;i<nRows;i++)


    } //public void roundUp(LCGrid2 lcGrid2,long simTS,double floodDepth)





    public Grid getGrid(String dataCategory) {

        Grid aGrid = new Grid(name+":"+dataCategory,1,units,null);

        Vector z2 = new Vector();

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

            Vector oneRow = (Vector) z.elementAt(i);
            Vector oneRow2 = new Vector();

            for(int j=0;j<oneRow.size();j++) {

                GridCell aCell = (GridCell) oneRow.elementAt(j);
                String oneCell = aCell.getValue(dataCategory);
                oneRow2.add(oneCell);

            } //for(int j=0;j<oneRow.size();j++)

            z2.add(oneRow2);

        } //for(int i=0;i<nRows;i++)

        aGrid.setGrid(xLLCorner,yLLCorner,cellSize,z2,nRows,nColumns,false);
        aGrid.gridIsRead(true);
        aGrid.setZMinMax();

        return aGrid;

    } //public Grid getGrid(String datacategory)



    public Vector getGridVector() {

        Vector vector = new Vector();
        vector.add(name);
        vector.add(fileName);

        return vector;
    }


    public String getFileName() {

        return fileName;

    }



} //public class Grid
