package iDSS.grids;

import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;



import iDSS.gridDisp.*;
import iDSS.utils.*;




//server classes
import helpers.*;


public class Grid implements GenericGrid, Serializable {

    private int type;
    private final int UNKNOWN = 0;
    private final int VALUE = 1;
    private final int CLASS = 2;

    private String name;

    private int units;
    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 int noDataValue = IDSSAppConstants.noDataValue;

    private String fileName = "";
    private File inFile,outFile;

    private Vector z;

    private double zMax = -9999999.0;
    private double zMin = 9999999.0;

    private Vector zRows,zColumns; //zRows is a vector of vector, contains rows
                                   //zColumns is a vector of double values
                                   //LL corner of the grid is 0,0


    private boolean isGridRead = false;
    private File tempZFile;
    private static int recursionLevel = 0;
    private static final int MAX_RECURSION_LEVEL = IDSSAppConstants.MAX_RECURSION_LEVEL;

    public Grid(String title, int gridType, int gridUnit, File inputFile) {

        //System.out.println("Creating grid " + title);

        name = title;
        if(inputFile != null) {
            inFile = inputFile;
            fileName = inFile.toString();
        }

        if ((gridType > 0) && (gridType < 3))
            type = gridType;
        else
            type = 0;

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

    } //public Grid(String title, int gridType, int gridUnit)





    public double getNoDataValue() {

        double aValue = (double) noDataValue;
        return aValue;

    }





    public Vector getZ() {

        return getDataVector();

    }//public Vector getZ()





    public String getName() {
        return name;
    }


    public int getType() {
        return type;
    }

    public int getUnits() {
        return units;
    }



    public void setName(String aName) {
        name = aName;
    }


    public void setType(int aType) {
        type = aType;
    }

    public void setUnits(int aUnit) {
        units = aUnit;
    }


    public double getCellSize() {
        return cellSize;
    }


    public void setCellSize(double size) {
        cellSize = size;
    }





    public int getNRows() {
        return nRows;
    }


    public void setNRows(int n) {
        nRows = n;
    }




    public int getNColumns() {
        return nColumns;
    }



    public void setNColumns(int n) {
        nColumns = n;
    }





    public double getxLLCorner() {
        return xLLCorner;
    }



    public void setxLLCorner(double xll) {
        xLLCorner = xll;
    }






    public double getyLLCorner() {
        return yLLCorner;
    }




    public void setyLLCorner(double yll) {
        yLLCorner = yll;
    }






    public double getZMin() {
        return zMin;
    }




    public double getZMax() {
        return zMax;
    }





    public Vector getGridVector() {

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

        return vector;
    }





    public Vector getDataVector() {

        Vector tempZ;

        if(z == null) {

            tempZ = readFromTempFile();

        }
        else {
            tempZ = z;
        }

        return tempZ;

    } //public Vector getDataVector()





    public void setDataVector(Vector v) {

        z = v;
        writeToTempFile();
        z = null;
    }





    public File getInFile() {
        return inFile;
    }




    public File getOutFile() {
        return outFile;
    }




    public void setOutFile(File file) {
        outFile = file;
    }






    public void setGrid(double llX, double llY, double cell, Vector zValues) {

        xLLCorner = llX;
        yLLCorner = llY;
        cellSize = cell;
        xURCorner = (nColumns-1) * cellSize;
        yURCorner = (nRows-1) * cellSize;
        z = zValues;

        writeToTempFile();
        z = null;
    }





    public void setGrid(double llX, double llY, double cell, Vector zValues,int rows,int columns) {

        xLLCorner = llX;
        yLLCorner = llY;
        cellSize = cell;
        xURCorner = (nColumns-1) * cellSize;
        yURCorner = (nRows-1) * cellSize;
        z = zValues;
        nRows = rows;
        nColumns = columns;

        writeToTempFile();
        z = null;

    }





    public void setGrid(double llX, double llY, double cell, Vector zValues,int rows,int columns,boolean writeToTempFile) {

        xLLCorner = llX;
        yLLCorner = llY;
        cellSize = cell;
        xURCorner = (nColumns-1) * cellSize;
        yURCorner = (nRows-1) * cellSize;
        z = zValues;
        nRows = rows;
        nColumns = columns;

        if(writeToTempFile) {
            writeToTempFile();
            z = null;
        }

    }









    private double getSecondToken(String line) {

        StringTokenizer st = new StringTokenizer(line," ");
        String aString;

        aString = st.nextToken();
        System.out.println(aString);
        aString = st.nextToken();
        System.out.println(aString);

        double aDouble = Double.parseDouble(aString);
        return aDouble;

    } //private int getSecondToken(String line)





    public String getFileName() {
        return fileName;
    }


    public void setRead(boolean b) {

        isGridRead = b;

    } //


    public void gridIsRead(boolean b) {
        isGridRead = b;
    }



    public boolean isRead() {
        return isGridRead;
    }





    public void read() {

        //System.out.println("Reading file..");

        isGridRead = true;

        String line;

        try {

            FileReader reader = new FileReader(inFile);
            BufferedReader buffer = new BufferedReader(reader);

            for(int i=0;i<=5;i++) {
                line = buffer.readLine();
                System.out.println(line);

                if (line != null) {
                    switch (i) {

                        case 0:
                            nColumns = (int) getSecondToken(line);
                            break;

                        case 1:
                            nRows = (int) getSecondToken(line);
                            break;

                        case 2:
                            xLLCorner = (double) getSecondToken(line);
                            break;

                        case 3:
                            yLLCorner = (double) getSecondToken(line);
                            break;

                        case 4:
                            cellSize = (double) getSecondToken(line);
                            break;

                        case 5:
                            noDataValue = (int) getSecondToken(line);
                            break;

                        default:

                    } //switch (i)

                } //if (line != null)

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

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

            StringTokenizer st;
            Vector rowVector;
            z = new Vector();

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

                line = buffer.readLine();

                rowVector = new Vector();
                //st = new StringTokenizer(line," ");
                st = new StringTokenizer(line);

                int j = 0;
                while (st.hasMoreTokens()) {
                    String aString = st.nextToken();
                    //System.out.println("adding " + aString + " to rowVector");
                    rowVector.add(aString);

                    if (Math.abs(Double.parseDouble(aString)-(double)noDataValue) > 0.1) {

                        //System.out.println("zMax and zMin");
                        if (Double.parseDouble(aString) < zMin)
                            zMin = Double.parseDouble(aString);

                        if (Double.parseDouble(aString) > zMax)
                            zMax = Double.parseDouble(aString);
                    }

                    j++;

                } //while (st.hasMoreTokens())

                if (nColumns != rowVector.size()) {
                    System.out.println("nColumns != data length at row " + i + " (row starts from 0)");
                    System.exit(0);
                } //if (nColumns != rowVector.size())

                z.add(rowVector);

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

            if(Math.abs(zMin-zMax) < 2) {

                zMax = zMax + 2;
            }

            buffer.close();
            reader.close();


        } //try

        catch(IOException ioe) {
            System.out.println("IOException has occured in Grid.read()" + ioe);
        } //catch(IOException ioe)


        writeToTempFile();
        z = null;

    } //private void read()











    public void read(URL url) {


        HttpURLConnection connection;
        InputStream input;
        //DataInputStream dataInput;
        BufferedReader buffer;
        int responseCode;

        isGridRead = true;

        String line;

        try {

            connection = (HttpURLConnection)url.openConnection();
            responseCode = connection.getResponseCode();


            if(responseCode != HttpURLConnection.HTTP_OK) {

                input = connection.getInputStream();
                //dataInput = new DataInputStream(input);

                buffer = new BufferedReader(new InputStreamReader(input));


                for(int i=0;i<=5;i++) {
                    line = buffer.readLine();
                    System.out.println(line);

                    if (line != null) {
                        switch (i) {

                            case 0:
                                nColumns = (int) getSecondToken(line);
                                break;

                            case 1:
                                nRows = (int) getSecondToken(line);
                                break;

                            case 2:
                                xLLCorner = (double) getSecondToken(line);
                                break;

                            case 3:
                                yLLCorner = (double) getSecondToken(line);
                                break;

                            case 4:
                                cellSize = (double) getSecondToken(line);
                                break;

                            case 5:
                                noDataValue = (int) getSecondToken(line);
                                break;

                            default:

                        } //switch (i)

                    } //if (line != null)

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

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

                StringTokenizer st;
                Vector rowVector;
                z = new Vector();

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

                    line = buffer.readLine();
                    rowVector = new Vector();
                    //st = new StringTokenizer(line," ");
                    st = new StringTokenizer(line);

                    int j = 0;
                    while (st.hasMoreTokens()) {
                        String aString = st.nextToken();
                        //System.out.println("adding " + aString + " to rowVector");
                        rowVector.add(aString);

                        if (Math.abs(Double.parseDouble(aString)-(double)noDataValue) > 0.1) {

                            //System.out.println("zMax and zMin");
                            if (Double.parseDouble(aString) < zMin)
                                zMin = Double.parseDouble(aString);

                            if (Double.parseDouble(aString) > zMax)
                                zMax = Double.parseDouble(aString);
                        }

                        j++;

                    } //while (st.hasMoreTokens())

                    if (nColumns != rowVector.size()) {
                        System.out.println("Grid.read(URL), nColumns != data length at row " + i + " (row starts from 0)");
                        //System.exit(0);
                    } //if (nColumns != rowVector.size())

                    z.add(rowVector);

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

                if(Math.abs(zMin-zMax) < 2) {

                    zMax = zMax + 2;
                }

                //dataInput.close();
                //reader.close();
                buffer.close();
                input.close();
                connection.disconnect();

            }//if(responseCode != HttpURLConnection.HTTP_OK)

        } //try




        catch(IOException ioe) {
            System.out.println("Grid.read(URL), IOException has occured in Grid.read()" + ioe);
        } //catch(IOException ioe)


        writeToTempFile();
        z = null;

    } //public void read(URL url)



















    public double getValue(double x,double y) {


        double xLLC = xLLCorner+cellSize/2;
        double yLLC = yLLCorner+cellSize/2;

        double xURC = xLLC+(nColumns-1)*cellSize;
        double yURC = yLLC+(nRows-1)*cellSize;

        double value = noDataValue;

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

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

            //System.out.println("col, row : " + col + ", " + row);

            double col1 = Math.floor(col);
            double col2 = Math.ceil(col);

            //System.out.println("col1, col2 : " + col1 + ", " + col2);

            double row1 = Math.floor(row);
            double row2 = Math.ceil(row);

            //System.out.println("row1, row2 : " + row1 + ", " + row2);

            int i1 = (new Double(col1)).intValue();
            int i2 = (new Double(col2)).intValue();

            int j1 = (new Double(row1)).intValue();
            int j2 = (new Double(row2)).intValue();

            //System.out.println("i1, i2 : " + i1 + ", " + i2);
            //System.out.println("j1, j2 : " + j1 + ", " + j2);

            double v11 = getValue(i1,j1);
            double v12 = getValue(i1,j2);
            double v21 = getValue(i2,j1);
            double v22 = getValue(i2,j2);

            //System.out.println("v11,v12,v21,v22 : " + v11 + ", " + v12 + ", " + v21 + ", " + v22);

            double x1 = xLLC+col1*((double)cellSize);
            double x2 = xLLC+col2*((double)cellSize);
            double y1 = yURC-row1*((double)cellSize);
            double y2 = yURC-row2*((double)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));

            double denom = 1/d11 + 1/d21 + 1/d12 + 1/d22;
            value = (v11/d11 + v12/d12 + v21/d21 + v22/d22)/denom;

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


        return value;

    } //private doube getValue(double x,double y)




    public synchronized double getValue(int col,int row) {

        double value = noDataValue;

        if(z != null) {

            if ((col>=0) && (col<nColumns) && (row>=0) && (row<nRows)) {

                Vector vector = (Vector) z.elementAt(row);
                String s = (String)vector.elementAt(col);
                Double d = new Double(s);
                value = d.doubleValue();

            } //if ((col>=0) && (col<nColumns) && (row>=0) && (row<nRows))

        } //if(z != null)

        else if (tempZFile != null){

            value = getValueFromTempFile(col,row);

        } //else if (tempZFile != null)

        return value;

    } //public double getValue(int col,int row)






    public synchronized int getIntValue(int col,int row) {

        int value = noDataValue;

        if(z != null) {

            if ((col>=0) && (col<nColumns) && (row>=0) && (row<nRows)) {

                Vector vector = (Vector) z.elementAt(row);
                String s = (String)vector.elementAt(col);
                Integer d = new Integer(s);
                value = d.intValue();

            } //if ((col>=0) && (col<nColumns) && (row>=0) && (row<nRows))

        } //ifif(z != null)

        else if (tempZFile != null){

            double d = getValueFromTempFile(col,row);
            String s = String.valueOf(Math.floor(d));
            StringTokenizer st = new StringTokenizer(s,".");

            if(st.hasMoreTokens()) {

                String s2 = st.nextToken();
                value = Integer.parseInt(s2);

            } //if

            else {

                value = 0;

            } //else

        } //else if (tempZFile != null)

        return value;

    } //public int getIntValue(int col,int row)






    public Vector getClasses() {

        //System.out.println("in Grid.getClasses()");
        Vector tempZ;
        if(!isGridRead)
            read();

        if(z == null)
            tempZ = readFromTempFile();
        else
            tempZ = z;

        Vector classTypes = new Vector();

        if (type == CLASS) {

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

                Vector currentRow = (Vector)tempZ.elementAt(i);

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

                    String s = (String) currentRow.elementAt(j);

                    if(!classTypes.contains(s)) {
                        classTypes.add(s);
                        System.out.println(s);
                    }

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

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

        } //if (gridType == CLASS)

        return classTypes;

    } //public Vector getClasses()











    public Grid createFloodGrid(double floodDepth,String name,Vector floodStartVector,File fileName) {

        //int floodStartRow;
        //int floodStartColumn;

        //System.out.println("In Grid.createFloodGrid()");
        Grid floodDepthGrid = new Grid(name,1,units,fileName);

        floodDepthGrid.setCellSize(cellSize);
        floodDepthGrid.setNRows(nRows);
        floodDepthGrid.setNColumns(nColumns);
        floodDepthGrid.setxLLCorner(xLLCorner);
        floodDepthGrid.setyLLCorner(yLLCorner);

/**
        double xLLC = xLLCorner+cellSize/2;
        double yLLC = yLLCorner+cellSize/2;

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

        Vector data = new Vector();
        double unknownFloodDepth = -1.0;
        String value = String.valueOf(unknownFloodDepth);


        //Fill up the grid with -1

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

            Vector oneRow = new Vector();

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

                oneRow.add(value);

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

            data.add(oneRow);

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


        floodDepthGrid.setDataVector(data);


        //System.out.println("Grid.createFloodGrid(), calling startFlooding, Row/Col of flood start : "+floodStartRow +"/"+floodStartColumn);


/**

        int origX = -99;
        int origY = -99;

        int startRow = floodStartRow;
        int startColumn = floodStartColumn;

        Vector oneRow = new Vector();
        oneRow.add(new Integer(floodStartRow));
        oneRow.add(new Integer(floodStartColumn));
        oneRow.add(new Integer(origX));
        oneRow.add(new Integer(origY));

        Vector rowColumns = new Vector();
        rowColumns.add(oneRow);
**/

        Vector rowColumns = getFloodStartVector(floodStartVector);

        System.out.println("Grid.createFloodGrid(), " + floodDepthGrid.getName());

        while(rowColumns.size() > 0) {

            Vector floodGridVector = startFlooding(floodDepth,floodDepthGrid,unknownFloodDepth,rowColumns);
            
            floodDepthGrid = (Grid)floodGridVector.elementAt(0);
            rowColumns = (Vector)floodGridVector.elementAt(1);

            //System.out.println("Grid.createFloodGrid(), rowColumns.size() : " + rowColumns.size());

        } //while(rowColumns.size() > 0)


        double dryValue = 0;
        floodDepthGrid.filterOut(unknownFloodDepth,dryValue); //turning unknown flood depth value to dry land

        floodDepthGrid.gridIsRead(true);
        return floodDepthGrid;

    } //public Grid createFloodGrid(double floodDepth,String name,double x,double y)







    private Vector getFloodStartVector(Vector floodStartVector) {

        Vector v = new Vector();

        double xLLC = xLLCorner+cellSize/2;
        double yLLC = yLLCorner+cellSize/2;

        double xURC = xLLC+(nColumns-1)*cellSize;
        double yURC = yLLC+(nRows-1)*cellSize;

        for(int ii=0;ii<floodStartVector.size();ii++) {

            double col;
            double row;

            Vector oneRow = (Vector)floodStartVector.elementAt(ii);

            double x = ((Double)oneRow.elementAt(0)).doubleValue();
            double y = ((Double)oneRow.elementAt(1)).doubleValue();

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

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

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

            else {

                Vector xCoords = new Vector();
                Vector yCoords = new Vector();

                xCoords.add(new Double(xLLC));
                xCoords.add(new Double(xURC));
                xCoords.add(new Double(xURC));
                xCoords.add(new Double(xLLC));

                yCoords.add(new Double(yLLC));
                yCoords.add(new Double(yLLC));
                yCoords.add(new Double(yURC));
                yCoords.add(new Double(yURC));

                double minDist = 99999999;
                double minX = xLLC;
                double minY = yLLC;

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

                    double xx = ((Double)xCoords.elementAt(i)).doubleValue();
                    double yy = ((Double)yCoords.elementAt(i)).doubleValue();

                    double d = Math.sqrt(Math.pow((x-xx),2)+Math.pow((y-yy),2));

                    if(d < minDist) {

                        minDist = d;
                        minX = xx;
                        minY = yy;

                    } //if(d < minDist)

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

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

            } //else


            col = Math.floor(col);
            row = Math.floor(row);

            int floodStartColumn = (new Double(col)).intValue();
            int floodStartRow = (new Double(row)).intValue();

            Vector oneRow2 = new Vector();

            oneRow2.add(new Integer(floodStartRow));
            oneRow2.add(new Integer(floodStartColumn));
            oneRow2.add(new Integer(-99));
            oneRow2.add(new Integer(-99));

            v.add(oneRow2);

/**
            System.out.println("x,y,floodStartRow,floodStartColumn,dem : " + x + "," + y + "," +
                                floodStartRow + "," + floodStartColumn + "," +
                                getValue(floodStartColumn,floodStartRow));
**/

        } //for(int ii=0;ii<floodStartVector.size();ii++)


        return v;


    } //private Vector getFloodStartVector(Vector floodStartVector)

















    private Vector startFlooding(double floodDepth,Grid floodDepthGrid,double unknownFloodDepth,Vector rowColumns) {


        //System.out.println("Grid.startFlooding()");

        Vector floodGridVector = new Vector();
        Vector rowColumns2 = new Vector();

        recursionLevel++;


        for(int ii=0;ii<rowColumns.size();ii++) {

            //System.out.println("Line 862");

            Vector oneRow = (Vector)rowColumns.elementAt(ii);
            int floodRow = ((Integer)oneRow.elementAt(0)).intValue();
            int floodColumn = ((Integer)oneRow.elementAt(1)).intValue();
            int origRow = ((Integer)oneRow.elementAt(2)).intValue();
            int origColumn = ((Integer)oneRow.elementAt(3)).intValue();


            if(isWithinRange(floodRow,floodColumn)) {

                //System.out.println("Line 870");
                double floodValue = (double) floodDepthGrid.getValue(floodColumn,floodRow);
                //System.out.println("flood grid value  = " + floodValue);

                if(Math.abs(floodValue-unknownFloodDepth) < 0.01) {

                    //System.out.println("Line 876");

                    double demValue = (double) getValue(floodColumn,floodRow);


                    if(Math.abs(demValue-(double)noDataValue)<0.01) {
                        floodDepthGrid.setDataAt(floodColumn,floodRow,(double)noDataValue);
                        //System.out.println("Nodata");
                    }

                    else if(demValue >= floodDepth) {
                        floodDepthGrid.setDataAt(floodColumn,floodRow,0);
                        //System.out.println("Dry");
                    }

                    else {

                        //System.out.println("Line 893");

                        double fd = floodDepth-demValue;
                        //System.out.println(recursionLevel + "  " + origRow + "  " + origColumn + "  " + floodRow + "  " + floodColumn);
                        floodDepthGrid.setDataAt(floodColumn,floodRow,fd);

                        for(int i=(floodRow-1);i<=(floodRow+1);i++) {

                            for(int j=(floodColumn-1);j<=(floodColumn+1);j++) {

                                //System.out.println("Line 903");

                                if((i == (origRow-1)) && (j == (origColumn-1))) {}
                                else if((i == (origRow-1)) && (j == (origColumn-0))) {}
                                else if((i == (origRow-1)) && (j == (origColumn+1))) {}
                                else if((i == (origRow-0)) && (j == (origColumn-1))) {}
                                else if((i == (origRow-0)) && (j == (origColumn-0))) {}
                                else if((i == (origRow-0)) && (j == (origColumn+1))) {}
                                else if((i == (origRow+1)) && (j == (origColumn-1))) {}
                                else if((i == (origRow+1)) && (j == (origColumn-0))) {}
                                else if((i == (origRow+1)) && (j == (origColumn+1))) {}
                                //else if((i == (floodRow)) && (j == (floodColumn))) {}         //No need

                                else {

                                    if(isWithinRange(i,j)) {

                                        //double fValue = (double) floodDepthGrid.getValue(i,j);

                                        //if(Math.abs(fValue-unknownFloodDepth) < 0.01) {

                                            Vector oneRow2 = new Vector();
                                            oneRow2.add(new Integer(i));
                                            oneRow2.add(new Integer(j));
                                            oneRow2.add(new Integer(floodRow));
                                            oneRow2.add(new Integer(floodColumn));

                                            rowColumns2.add(oneRow2);

                                        //} //if(Math.abs(fValue-unknownFloodDepth) < 0.01)
                                
                                    } //if(isWithinRange(i,j))

                                } //else

                            } //for(int j=jMin;j<=jMax;j++)

                        } //for(int i=iMin;i<=iMax;i++)

                    } //else

                } //if(Math.abs(floodValue-unknownFloodDepth) < 0.01)

            } //if(isWithinRange(floodRow,floodColumn))


        } //for(int ii=0;ii<rowColumns.size();ii++)


        while(rowColumns2.size() > 0) {

            //System.out.println(recursionLevel);

            if(recursionLevel >= MAX_RECURSION_LEVEL) {

                //System.out.println("Grid.startFlooding(), recursionLevel : " + recursionLevel);
                recursionLevel = 0;
                break;

            } //if(recursionLevel >= MAX_RECURSION_LEVEL)

            floodGridVector = startFlooding(floodDepth,floodDepthGrid,unknownFloodDepth,rowColumns2);
            floodDepthGrid = (Grid)floodGridVector.elementAt(0);
            rowColumns2 = (Vector)floodGridVector.elementAt(1);


        } //while(rowColumns2.size() > 0)

        floodGridVector.add(floodDepthGrid);
        floodGridVector.add(rowColumns2);

        return floodGridVector;

    } //private Grid startFlooding(double floodDepth,Grid floodDepthGrid,int floodStartRow,int floodStartColumn)













    private boolean isWithinRange(int i,int j) {

        return ((i >= 0) && (i <= (nRows-1)) && (j >= 0) && (j <= (nColumns-1)));

    } //private boolean isWithinRange(int i,int j)








    public synchronized void setDataAt(int col,int row,double d) {

        if(z != null) {

            Vector oneRow = (Vector) z.elementAt(row);
            oneRow.remove(col);
            oneRow.insertElementAt(String.valueOf(d),col);
            z.remove(row);
            z.insertElementAt(oneRow,row);

        } //if()

        else {

            setValueToTempFile(col,row,d);

        } //

    } //private void setDataAt(int col,int row,double d)









    //This method turns unknown flood depth cells to dry
    //To be run after startFlooding()
    //The logic is cell remaining with unknown flooding will in effect be dry

    private void filterOut(double unknownFloodDepth,double newValue) {

        Vector tempZ;

        if (z == null)
            z = readFromTempFile();

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

            Vector currentRow = (Vector) z.elementAt(i);

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

                String s = (String) currentRow.elementAt(j);
                double value = Double.parseDouble(s);

                if(Math.abs(value-unknownFloodDepth) < 0.1) {

                    setDataAt(j,i,newValue);

                } //if(Math.abs(value-unknownFloodDepth) < 0.1)

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


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


        writeToTempFile();
        z = null;

    } //private void filterOut(double unknownFloodDepth)
















    public void writeGridObject() {


        if(outFile == null) {

            outFile = FileNameDialog.getOutputFileName("Save",null,null);
            inFile = outFile;

        }//if(outFile == null)


        if(outFile != null) {

            if(z == null)
                z = readFromTempFile();

            ObjectReadWrite.write(outFile,this);


            z = null;

        }//if(outFile != null)


    } //public void writeGridObject(ObjectOutputStream ooStream)










    public static Grid readGridObject(File aFile) {


        Grid aGrid = new Grid("no title",0,0,aFile);

        if(aFile.exists()) {
            aGrid = (Grid)ObjectReadWrite.read(aFile);
        }

        else {
            System.out.println("Grid.readGridObject, input file " + aFile.toString() + " doesn't exist");
        }


        aGrid.gridIsRead(true);
        aGrid.setZMinMax();
        aGrid.writeToTempFile();
        return aGrid;


    } //public static void readGridObject()








    public void describe() {

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

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

        Vector tempZ;
        if (z == null)
            tempZ = readFromTempFile();
        else
            tempZ = z;

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

            Vector currentRow = (Vector) tempZ.elementAt(i);

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

                String s = (String)currentRow.elementAt(j);
                double value = Double.parseDouble(s);
                System.out.print(" " + value);

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

            System.out.println("");

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

        tempZ = null;

    } //public void describe()









    public Vector showdescription() {

        Vector description = new Vector();
        Vector tempZ;

        description.add("Name        = " + name);
        description.add("\nnRows       = " + nRows);
        description.add("\nnColumns    = " + nColumns);
        description.add("\nxLLCorner   = " + xLLCorner);
        description.add("\nyLLCorner   = " + yLLCorner);
        description.add("\ncellSize    = " + cellSize);
        description.add("\nnoDataValue = " + noDataValue);

        if (z == null)
            tempZ = readFromTempFile();
        else
            tempZ = z;

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

            Vector currentRow = (Vector) tempZ.elementAt(i);
            String oneRow = "";

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

                String s = (String)currentRow.elementAt(j);
                oneRow = oneRow.concat(" " + s);

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

            description.add("\n"+oneRow);

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

        tempZ = null;
        return description;

    } //public Vector showdescription()






    public void setZMinMax() {

        Vector tempZ;
        if(z == null)
            tempZ = readFromTempFile();
        else
            tempZ = z;

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

            Vector currentRow = (Vector) tempZ.elementAt(i);

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

                String s = (String) currentRow.elementAt(j);
                double value = Double.parseDouble(s);

                if (Math.abs(value-(double)noDataValue) > 0.1) {

                    zMin = Math.min(zMin,value);
                    zMax = Math.max(zMax,value);

                } //if (Math.abs(value-(double)noDataValue) > 0.1)

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

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

        zMin = Math.floor(zMin);
        zMax = Math.ceil(zMax);


    } //public void setZMinMax()





    public void draw(GridDisplay gridDisplay,SurfaceColor sc) {


        if(IDSSAppConstants.debug)
            System.out.println("Grid.draw()");


        BufferedImage bi = getImage(sc);

        gridDisplay.refreshCanvas(bi);

    } //public void draw(Grid grid,SurfaceColor sc)







    public BufferedImage getImage(SurfaceColor sc) {

        if(IDSSAppConstants.debug)
            System.out.println("Grid.getImage()");


        if (!isRead())
            read();


        BufferedImage bi = new BufferedImage (getNColumns(),getNRows(),BufferedImage.TYPE_INT_RGB);
        Graphics g2 = (Graphics) bi.getGraphics();

        double noData = getNoDataValue();

        Vector tempZ;
        if(z==null)
            tempZ = readFromTempFile();
        else
            tempZ = z;

        for(int i=0;i<tempZ.size();i++) { //rows of the grid

            Vector currentRow = (Vector) tempZ.elementAt(i);

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

                double zValue = Double.parseDouble(currentRow.elementAt(j).toString());

                if (Math.abs(zValue-noData) > 0.1) {

                    Color color = sc.getColor(zValue);
                    g2.setColor(color);
                    g2.fillRect(j,i,1,1);

                } //if (Math.abs(zValue-noData) > 0.1)

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


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

        tempZ = null;
        return bi;

    } //public BufferedImage getImage(SurfaceColor sc)




    public ServerGrid getServerGrid() {
        Vector tempZ;
        if(z == null)
            tempZ = readFromTempFile();
        else
            tempZ = z;

        ServerGrid sg = new ServerGrid(name,type,units,fileName,cellSize,nRows,nColumns,
                                        xLLCorner,yLLCorner,noDataValue,tempZ);

        return sg;

    } //public ServerGrid getServerGrid()




    public void readServerGrid(ServerGrid sg) {

        name = sg.getName();
        type = sg.getType();
        units = sg.getUnits();
        fileName = sg.getFileName();
        cellSize = sg.getCellSize();
        nRows = sg.getNRows();
        nColumns = sg.getNColumns();
        xLLCorner = sg.getXLLCorner();
        yLLCorner = sg.getYLLCorner();
        noDataValue = sg.getNoDataValue();
        z = sg.getZ();

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

        inFile = new File(fileName);
        outFile = new File(fileName);
        writeToTempFile();
        z = null;

    } //public void readServerGrid(ServerGrid sg)





    public void writeToTempFile() {

        System.out.println("Grid.writeToTempFile(), IDSSAppConstants.tempDir : " + IDSSAppConstants.tempDir);


        File tempDir = new File(IDSSAppConstants.tempDir);
        if(!tempDir.exists())
            tempDir.mkdirs();

        tempZFile = new File(tempDir,FileNameDialog.getTempFileName());

        if(tempZFile.exists())
            tempZFile.delete();

        tempZFile.deleteOnExit();



        try {

            RandomAccessFile demRAF = new RandomAccessFile(tempZFile,"rw");

            if(demRAF == null) {
                System.out.println("demRAF is null");
                System.exit(0);

            }//

            if(z == null) {
                System.out.println("z is null");
                System.exit(0);

            }//

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

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

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

                    String s = (String)oneRow.elementAt(j);
                    float f = (float)IDSSAppConstants.noDataValue;

                    if(!s.equals("NaN"))
                        f = Float.parseFloat(s);

                    demRAF.writeFloat(f);
                    //System.out.println(i + " " + j + " " + demRAF.getFilePointer() + "  " + f);

                } //for(int j=0;j<demGrid.getNColumns();j++)


            } //for (int i=0;i<demGrid.getNRows();i++)

            z = null;
            demRAF.close();

        } //try

        catch (Exception e) {

            System.out.println("Grid.writeToTempFile(), e: " + e.toString());
            System.exit(0);

        } //catch(Exception e)

    } //private void writeToTempFile()






    private Vector readFromTempFile() {

        //System.out.println("Grid.readFromTempFile()");
        Vector tempZ = null;

        try {

            RandomAccessFile demRAF = new RandomAccessFile(tempZFile,"r");
            tempZ = new Vector();

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

                Vector oneRow = new Vector();

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

                    float f = demRAF.readFloat();
                    String s = String.valueOf(f);
                    oneRow.add(s);
                    //System.out.println(i + "  " + j + "  " + f);

                } //for(int j=0;j<demGrid.getNColumns();j++)

                tempZ.add(oneRow);

            } //for (int i=0;i<demGrid.getNRows();i++)

            demRAF.close();


        } //try

        catch (Exception e) {

            System.out.println("Grid.readFromTempFile(), e: " + e.toString());
            System.exit(0);

        } //catch(Exception e)

        return tempZ;

    } //private Vector readFromTempFile()






    private double getValueFromTempFile(int col,int row) {

        //System.out.println("Grid.getValueFromTempFile()");
        //System.out.println("col,row : " + col + "," + row);
        double d = (double)noDataValue;

        try {

            RandomAccessFile demRAF = new RandomAccessFile(tempZFile,"r");
            long pos = (row*getNColumns()+col)*4;
            demRAF.seek(pos);
            float f = demRAF.readFloat();
            d = (double)f;
            demRAF.close();

        } //

        catch (Exception e) {

            System.out.println("Grid.getValueFromTempFile(), e: " + e.toString());
            System.exit(0);

        } //catch(Exception e)

        //System.out.println("value : " + d);
        return d;

    } //private double getValueFromTempFile(int col,int row)




    private void setValueToTempFile(int col,int row,double d) {

        //System.out.println("Grid.setValueToTempFile()");
        //System.out.println("col,row,d : " + col + "," + row + "," + d);

        try {

            RandomAccessFile demRAF = new RandomAccessFile(tempZFile,"rw");
            long pos = (row*getNColumns()+col)*4;
            demRAF.seek(pos);
            float f= (new Double(d)).floatValue();
            demRAF.writeFloat(f);
            demRAF.close();

        } //

        catch (Exception e) {

            System.out.println("Grid.setValueToTempFile(), e: " + e.toString());
            System.exit(0);

        } //catch(Exception e)



    } //private void setValueToTempFile(int col,int row,double d)


} //public class Grid

