package usda.weru.erosion;

import com.klg.jclass.table.beans.DataWrapper;
import com.klg.jclass.table.*;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;
import java.text.NumberFormat;


/*
 * WindGenDisp.java
 *
 * Created on July 26, 2005, 1:52 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */
/**
 *
 * @author wjr
 */
public class WindDisp extends usda.weru.erosion.gui.WindDisp_n {

    private static final long serialVersionUID = 1L;

    private static final int numRows = 4;
    private static final int numCols = 17;
//	private double threshold = 8.0;
    private final double threshold = 8.0;
    private static final int numMonths = 12;
    private static final int numDirections = 16;

    /* The boundaries array does not have a 0.5 category which the
     * fortran program does have. This is left out so that the 1.5
     * average corresponds to the first entry in the wind speed table.
     */
    private static final double[] boundaries = {1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5,
        9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5,
        18.5, 19.5, 20.5, 25.5, 30.5, 35.5, 40.5, 45.5};

    private static final String[] dirNames = {"N-0", "NNE-22", "NE-45", "ENE-67", "E-90", "ESE-112", "SE-135", "SSE-157",
        "S-180", "SSW-202", "SW-225", "WSW-247", "W-270", "WNW-292", "NW-315", "NNW-337", "Total"};

//	private final String[] dirNames = {"North-0", "NNE-22.5", "NE-45", "ENE-67.5", "East-90", "ESE-112.5", "SE-135", "SSE-157.5",
//	"South-180", "SSW-202.5", "SW-225", "WSW-247.5", "West-270", "WNW-292.5", "NW-315", "NNW-337.5"};
    private static final int WIND_SPEED = 0;
//	private static final int WIND_ENERGY = 1;
//	private static final int WIND_FRACT = 2;

//    private String[] monthNames = {"January", "February", "March", "April", "May", "June",
//            "July", "August", "September", "October", "November", "December"};
    private static final String[] rowLbls = {"Threshold (m/s)", "Winds > Thresh (%)", "Dir Prob (%)", "Thresh Prob (%)"};

    private String[][] cellData = null;

    /*static*/ Vector<Vector<String>> dbVec = new Vector<>();
    /*static*/ Vector<String> staVec = new Vector<>();
    double elevation = 1000.0; // default elevation in meters
    double temperature = 20.0; // default temperature Celsius
    double stepSize = 0.1; // width of integrating box

    /*
     * Creates a new instance of WindDisp.
     *
     * @arg file name for wind data file
     * */
    /**
     *
     * @param fileName
     */
    public WindDisp(String fileName) {
//System.out.println("WD: filename " + fileName);        
        cellData = new String[numRows][];
        for (int rdx = 0; rdx < cellData.length; rdx++) {
            cellData[rdx] = new String[numCols];
            for (int cdx = 0; cdx < cellData[rdx].length; cdx++) {
                cellData[rdx][cdx] = "a";
            }
        }
        loadWindStationData(fileName);
        cellData[0][0] = "" + dbVec.size();
        cellData[0][1] = "" + staVec.size();
        DataWrapper dw = new DataWrapper(numRows, numCols, rowLbls, dirNames, cellData);
        jcTable.setMaxWidth(JCTableEnum.ALLCELLS, 70);
        jcTable.setSize(720, 140);
        JCCellStyle cs = new JCCellStyle();
        cs.setHorizontalAlignment(JCTableEnum.RIGHT);
        cs.setEditable(false);
        java.awt.Color col = new java.awt.Color(1.0f, 1.0f, 1.0f);
        cs.setBackground(col);
        JCCellRange cr = new JCCellRange(0, 0, numRows - 1, numCols - 1);
        jcTable.setCellStyle(cr, cs);
        jcTable.setData(dw);
    }

    /**
     *
     */
    public WindDisp() {
////System.out.println("WD: filename " + fileName);        
        cellData = new String[numRows][];
        for (int rdx = 0; rdx < cellData.length; rdx++) {
            cellData[rdx] = new String[numCols];
            for (int cdx = 0; cdx < cellData[rdx].length; cdx++) {
                cellData[rdx][cdx] = "a";
            }
        }
//        loadWindStationData(fileName);
        cellData[0][0] = "" + dbVec.size();
        cellData[0][1] = "" + staVec.size();
        DataWrapper dw = new DataWrapper(numRows, numCols, rowLbls, dirNames, cellData);
        jcTable.setMaxWidth(JCTableEnum.ALLCELLS, 70);
        jcTable.setSize(720, 140);
        JCCellStyle cs = new JCCellStyle();
        cs.setHorizontalAlignment(JCTableEnum.RIGHT);
        cs.setEditable(false);
        java.awt.Color col = new java.awt.Color(1.0f, 1.0f, 1.0f);
        cs.setBackground(col);
        JCCellRange cr = new JCCellRange(0, 0, numRows - 1, numCols - 1);
        jcTable.setCellStyle(cr, cs);
        /*		JCCellStyle cs2 = new JCCellStyle();
         cs2.setHorizontalAlignment(JCTableEnum.RIGHT);
         cs2.setEditable(false);
         col = new java.awt.Color(1.0f, 1.0f, 1.0f);
         cs2.setBackground(col);
         java.awt.Font cf = cs2.getFont();
         cf = cf.deriveFont((float) (cf.getSize()*0.8));
         cs2.setFont(cf);
         cs2.setVerticalAlignment(cs2.BOTTOM);
         JCCellRange cr2 = new JCCellRange(WIND_DIRECTION, 0, WIND_DIRECTION, numCols-1);
         jcTable.setCellStyle(cr2, cs2);*/
        jcTable.setData(dw);
    }

    /*
     * Constructor
     *
     * @arg args is String array -- args[0] is station number, args[1] is
     * file name
     */
    /**
     *
     * @param args
     */
    public WindDisp(String[] args) {
////System.out.println("WGD_WGD: " + java.lang.management.MemoryUsage.toString());
//		if (args.length > 2) { // threshold specified
//			threshold = Double.parseDouble(args[2]);
//		}
        this(args[1]);
//		try {
////			//System.out.println("WGD: htsize " + args[0] + HT_stations.get(args[0]) + " " + HT_stations.size());
//			StationRec rc = (StationRec) HT_stations.get(args[0]);
////			if (rc != null) JCB_stationName.setSelectedItem(rc);
//		} catch (ArrayIndexOutOfBoundsException e) {
//			//System.err.println("WGD: " + ">" + args[0] + "<" + e);
//			e.printStackTrace();
//		}
    }

    static class StationRec implements Comparable<Object> {
//		String statID = "000000";

        String statDesc = "desc";
        int statIdx = 0;

        StationRec(String id, String desc, int stat) {
//			statID = id;
            statDesc = desc;
            statIdx = stat;
        }

        @Override
        public String toString() {
            return statDesc;
        }

        @Override
        public int compareTo(Object compTo) {
            return statDesc.compareTo(((StationRec) compTo).statDesc);
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof StationRec)) {
                return false;
            }
            StationRec sr = (StationRec) o;
            return this.statDesc.equals(sr.statDesc);
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 71 * hash + (this.statDesc != null ? this.statDesc.hashCode() : 0);
            hash = 71 * hash + this.statIdx;
            return hash;
        }
    }

    private final Hashtable<String, StationRec> HT_stations = new Hashtable<>();
    private List<StationRec> staList = null;

    /**
     *
     * @param fileName
     */
    public void loadWindStationData(String fileName) {
//System.out.println("WD_lD: filename " + fileName);        

        int staCnt = 1;
//		Vector staList = new Vector();

        if (staList == null) {
            staList = new LinkedList<>();
            BufferedReader in = null;
            try {
////System.out.println("WD_lD: " + fileName);
                in = new BufferedReader(new TFileReader(new TFile(fileName)));
//				new File("C:/weps/weps1.install/db/wind_gen", "wind_test.wdb")));

                while (true) {
                    String inpStr;
                    while ((inpStr = in.readLine()) != null) {
////System.out.println("WD_lD: " + inpStr);						
                        if (inpStr.length() == 0) {
                            continue;
                        }
                        if (inpStr.charAt(0) != '#') {
                            staVec.add(inpStr);
                            continue;
                        } else {
                            StringTokenizer st = new StringTokenizer(inpStr, " ");
                            st.nextToken();
                            String stationID = st.nextToken();
                            StringBuffer sb = new StringBuffer();
//							inpStr = "";
                            while (st.hasMoreTokens()) {
                                sb.append(st.nextToken());
                                sb.append(" ");
//								inpStr += st.nextToken() + " ";
                            }
//							StationRec sr = new StationRec(stationID, inpStr, staCnt);
                            StationRec sr = new StationRec(stationID, sb.toString().trim(), staCnt);
                            staList.add(sr);
                            HT_stations.put(stationID, sr);
                            dbVec.add(staVec);
                            staVec = new Vector<>();
                            staCnt++;
                        }
                    }
                    dbVec.add(staVec);
                    if (inpStr == null) {
                        break;
                    }
                }
            } catch (IOException e) {
                //System.err.println("WGD_lD: " + e);
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            Collections.sort(staList);
        }
//		for (Iterator it = staList.iterator(); it.hasNext(); ) {
//			JCB_stationName.addItem(it.next());
//		}
//		JCB_month.removeAllItems();
//		for (int mdx = 0; mdx < monthNames.length; mdx++) {
//			JCB_month.addItem(monthNames[mdx]);
//		}
//		JCB_month.removeItemAt(0);
//		JCB_month.setSelectedIndex(0);
    }

//	private ConfigData cd = null;
    /**
     *
     * @param args
     */
    public static void main(String[] args) {

        WindDisp wgd = null;
        if (args.length > 0) {
            wgd = new WindDisp(args);
        } else {
            wgd = new WindDisp("C:/weps/weps1.install/db/wind_gen/wind_test.wdb");
        }
        wgd.setVisible(true);
    }

    /**
     *
     * @param stationID
     * @return
     */
    public int findStation(String stationID) {
        stationID = stationID.trim();
        StationRec rc = HT_stations.get(stationID);
        //System.out.println("WGD_fS: " + rc + " " + stationID + " " + HT_stations.size());
        return rc.statIdx;
    }

    /**
     *
     * @param staIdx
     */
    public void parseElevation(int staIdx) {
        Vector<String> selVec = dbVec.get(staIdx);
        String inpLin = selVec.get(0); //elevation in first line
        StringTokenizer st = new StringTokenizer(inpLin);
        for (int edx = 0; st.hasMoreTokens() && edx < 6; edx++) {
            st.nextToken();
        }
        String elevStr = st.nextToken();
//		JL_Elevation.setText(elevStr);
        elevation = Double.parseDouble(elevStr);
    }

    private double[][] windTimes = null;

    /**
     *
     * @return
     */
    public double[][] getWindTimes() {
        return windTimes.clone();
    }

    /**
     *
     * @param staIdx
     */
    public void parseWindTimes(int staIdx) {
        Vector<String> selVec = dbVec.get(staIdx);
        windTimes = new double[numDirections][];
        for (int ddx = 0; ddx < numDirections; ddx++) {
            String[] inpArr = new String[numMonths];
            windTimes[ddx] = new double[numMonths];
            String inpLin = selVec.get(ddx + 1); //skip the first line, use next 16
            StringTokenizer st = new StringTokenizer(inpLin);
            for (int mdx = 0; st.hasMoreTokens(); mdx++) {
                inpArr[mdx] = st.nextToken();
                if (ddx == 0) {
                    cellData[ddx][mdx] = inpArr[mdx];
                }
                windTimes[ddx][mdx] = Double.parseDouble(inpArr[mdx]);
            }
        }
    }

    private double[][][] cumProb = null;
    private double[][][] intvProb = null;

    /**
     *
     * @return
     */
    public double[][][] getCumProb() {
        return cumProb.clone();
    }

    /**
     *
     * @param staIdx
     */
    public void parseWindSpeeds(int staIdx) {
        Vector<String> selVec = dbVec.get(staIdx);
        cumProb = new double[numMonths][][];
        intvProb = new double[numMonths][][];
        for (int mdx = 0; mdx < numMonths; mdx++) {
            cumProb[mdx] = new double[numDirections][];
            intvProb[mdx] = new double[numDirections][];
            for (int ddx = 0; ddx < numDirections; ddx++) {

                // load cumulative probability curve for each direction
                String inpLin = selVec.get(mdx * numDirections + ddx + 18); //skip the direction data, use next 16*12
                StringTokenizer st = new StringTokenizer(inpLin);
                int numTok = st.countTokens();
                String[] inpArr = new String[numTok];
                cumProb[mdx][ddx] = new double[numTok + 2];
                intvProb[mdx][ddx] = new double[numTok + 2];
                for (int pdx = 0; st.hasMoreTokens(); pdx++) {
                    inpArr[pdx] = st.nextToken();
                    if (mdx == 0 && ddx == 0 && pdx < cellData[1].length) {
                        cellData[1][pdx] = inpArr[pdx];
                    }
                    cumProb[mdx][ddx][pdx] = Double.parseDouble(inpArr[pdx]);
                }
                cumProb[mdx][ddx][numTok] = 1.0; // put 1 in last 2 positions
                cumProb[mdx][ddx][numTok + 1] = 1.0;

                // convert cumulative curve to interval values
                intvProb[mdx][ddx] = new double[numTok + 2];
                for (int pdx = 1; pdx < cumProb[mdx][ddx].length; pdx++) {
                    try {
                        double deltaY = cumProb[mdx][ddx][pdx] - cumProb[mdx][ddx][pdx - 1];
                        double deltaX = boundaries[pdx] - boundaries[pdx - 1];
                        intvProb[mdx][ddx][pdx] = deltaY / deltaX;
                    } catch (ArrayIndexOutOfBoundsException f) {
						// wind prob array > # of boundaries -- skip it
                        //System.err.println("WGD_pWD: " + f);
                    }
                }
            }
        }
    }

    private void calcThreshold() {
        int tdx = 0;

        while (boundaries[tdx] < threshold && tdx < boundaries.length) {
            tdx++;
        }

        for (int mdx = 0; mdx < numMonths; mdx++) {
            double overThreshold = 0.0;
            for (int ddx = 0; ddx < numDirections; ddx++) {
                try {
					// find interpolation fraction between two boundary
                    // points
                    double interp = (threshold - boundaries[tdx - 1])
                            / (boundaries[tdx] - boundaries[tdx - 1]);
					// calculate the interpolated value for winds over
                    // theshold and multiply by the fraction of time
                    // the wind is coming from that direction and
                    // normalize by dividing by number of wind
                    // directions (16);
                    double windSpeedProb = 1 - (cumProb[mdx][ddx][tdx - 1]
                            + interp * (cumProb[mdx][ddx][tdx] - cumProb[mdx][ddx][tdx - 1]));
                    double addOn = windSpeedProb * windTimes[ddx][mdx] / 100;
                    overThreshold += addOn;

                } catch (ArrayIndexOutOfBoundsException e) {
					// if array doesn't have any winds over threshold,
                    // just skip that direction
                }
            }

            NumberFormat cf = NumberFormat.getNumberInstance(Locale.US);
            cf.setMaximumFractionDigits(1);
            cf.setMinimumFractionDigits(1);

            cellData[WIND_SPEED][mdx] = cf.format(overThreshold * 100);
        }
    }

    private void setThreshold() {
//		JL_Threshold.setText(threshold + " m/s ");
    }

//	private String[][] getStationData(int staIdx) {
//		setThreshold();
//		parseElevation(staIdx);
//		parseWindTimes(staIdx);
//		parseWindSpeeds(staIdx);
////		calcThreshold();
////		calcEWP();
////		calcPrevailingWind();
//		return cellData;
//	}
    double[] inpWS;
    double[][] inpPT;
    double[][] inpWT;

    private void changeDisplayMonth() {
//		if (JCB_month.getItemCount() == 13) {
//			JCB_month.removeItemAt(0);
//			JCB_month.setSelectedIndex(0);
////System.out.println("WD_cDM: remove item 1");			
//		}
        int mdx = JCB_month.getSelectedIndex();
//		if (mdx == 0) mdx++;
//		mdx--;
//System.out.println("WD_cDM: " + JCB_month.getItemCount());
        java.text.NumberFormat nf = java.text.NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(2);
        nf.setMinimumFractionDigits(2);

        double dirTotal = 0.0;
        double probTotal = 0.0;
        for (int ddx = 0; ddx < inpWS.length; ddx++) {
            //System.out.print(" " + inpWS[ddx]);			
            cellData[0][ddx] = "" + inpWS[ddx];
            cellData[1][ddx] = "" + nf.format(inpPT[mdx][ddx] * 100);
            cellData[2][ddx] = "" + nf.format(inpWT[ddx][mdx]);
            cellData[3][ddx] = "" + nf.format(inpPT[mdx][ddx] * inpWT[ddx][mdx]);
            dirTotal += inpWT[ddx][mdx];
            probTotal += inpPT[mdx][ddx] * inpWT[ddx][mdx];
        }
        cellData[0][inpWS.length] = ""; //nf.format(dirTotal);
        cellData[1][inpWS.length] = ""; //nf.format(dirTotal);
        cellData[2][inpWS.length] = nf.format(dirTotal);
        cellData[3][inpWS.length] = nf.format(probTotal);
        DataWrapper dw = new DataWrapper(numRows, numCols, rowLbls, dirNames, cellData);
        jcTable.setData(dw);
        //System.out.println();		
    }

    /**
     *
     * @param windSpeeds
     * @param probTable
     * @param windTimes
     */
    public void setCellData(double[] windSpeeds, double[][] probTable, double[][] windTimes) {
        inpWS = windSpeeds.clone();
        inpPT = probTable.clone();
        inpWT = windTimes.clone();
        changeDisplayMonth();
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void JCB_monthActionPerformed(java.awt.event.ActionEvent evt) {
        changeDisplayMonth();
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void JB_prevActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_JB_prevActionPerformed
        int idx = JCB_month.getSelectedIndex();
        if (idx == 0) {
            return;
        }
        JCB_month.setSelectedIndex(idx - 1);
    }//GEN-LAST:event_JB_prevActionPerformed

    /**
     *
     * @param evt
     */
    @Override
    protected void JB_nextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_JB_nextActionPerformed
        int idx = JCB_month.getSelectedIndex();
        if (idx == 11) {
            return;
        }
        JCB_month.setSelectedIndex(idx + 1);
    }//GEN-LAST:event_JB_nextActionPerformed

    /**
     *
     * @param evt
     */
    @Override
    protected void formComponentResized(java.awt.event.ComponentEvent evt) {
        super.formComponentResized(evt);;
        //System.out.println("WD_fCR: " + evt);
        //System.out.println("WD_fCR: " + jcTable.getSize());
        java.awt.Dimension jctDim = jcTable.getSize();
        int wid = ((javax.swing.JFrame) evt.getSource()).getSize().width;
        jctDim.width = wid - 30;
        jcTable.setSize(jctDim);
        //System.out.println("WD_fCR: " + jcTable.getSize());
    }
}
