package iDSS.sm;

import java.util.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.awt.image.BufferedImage;
import java.sql.Timestamp;
import javax.swing.event.*;
import javax.swing.table.*;
import java.beans.*;


import javax.swing.table.AbstractTableModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;


import iDSS.gridDisp.*;
import iDSS.disp.*;
import iDSS.tables.*;
import iDSS.utils.*;



public class TimeVariantSurvivalModel implements SurvivalModel, Serializable,
                                        PropertyChangeListener, ActionListener {


    private Hashtable models,timestamps;
    private String modelName;


    private PropertyChangeSupport pcs;
    private JComboBox modelsCombo;
    private DrawingPanel2 picturePanel;

    private JButton defineButton;
    private JButton detailsButton;
    private JButton disposeButton;
    private JButton acceptButton;

    private JButton userSMButton;


    private int currentMonth,currentDay;
    private String currentModel;
    private JTable table;

    private Calendar modelCalendar;
    private JScrollPane tableScrollPane;

    private DisplayFrame internalFrame;





    public TimeVariantSurvivalModel() {

        modelName = "Timevariant Model";

        models = new Hashtable();
        timestamps = new Hashtable();

        pcs = new PropertyChangeSupport(this);

        modelCalendar = Calendar.getInstance();
        modelCalendar.set(1970,0,1,0,0,0);

    } //public TimeVariantSurvivalModel()





    public void setModels(Hashtable modelsHashtable) {

        models = modelsHashtable;

    } //public void setModels(Hashtable modelsHashtable)



    public void setTimestamps(Hashtable hashtable) {

        timestamps = hashtable;

    } //public void setTimestamps(Hashtable hashtable)



    public void setModelCalendar(Calendar c) {

        modelCalendar = c;

    } //public void setModelCalendar(Calendar c)








    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 String getName() {

        return modelName;

    } //public String getName()



    private long getYearinMillis() {

        Calendar c1 = Calendar.getInstance();
        c1.set(1970,0,1,0,0,0);
        long t1 = c1.getTime().getTime();

        Calendar c2 = Calendar.getInstance();
        c2.set(1971,0,1,0,0,0);
        long t2 = c2.getTime().getTime();

        long yearInMillis = t2-t1;
        return yearInMillis;

    } //private long getYearinMillis()






    private Timestamp switchYear(Timestamp from,Timestamp to) {

        Calendar cFrom = Calendar.getInstance();
        cFrom.setTime(from);
        int fromYear = cFrom.get(Calendar.YEAR);

        Timestamp to2 = switchYear(fromYear,to);
        return to2;

    } //private Timestamp switchYear(Timestamp from,Timestamp to)






    private Timestamp switchYear(int fromYear,Timestamp to) {

        Calendar cTo = Calendar.getInstance();
        cTo.setTime(to);
        cTo.set(Calendar.YEAR,fromYear);

        Timestamp to2 = new Timestamp(cTo.getTime().getTime());
        return to2;

    } //private Timestamp switchYear(int fromYear,Timestamp to)





    private Vector getBoundingModels(Timestamp sourceTime) {


        Vector boundingModels = new Vector();

        Timestamp t1970 = switchYear(1970,sourceTime);

        Timestamp t1 = null;
        Timestamp t2 = null;
        SurvivalModel sm1 = null;
        SurvivalModel sm2 = null;

        long diff1 = getYearinMillis(); //number of millisecs in a year
        long diff2 = diff1;

        //System.out.println("TimeVariantSurvivalModel.getBoundingModels(), diff1 = " + diff1);
        //System.out.println("TimeVariantSurvivalModel.getBoundingModels(), t1970 = " + t1970.toString());

        Enumeration hashTimes = models.keys();

        while(hashTimes.hasMoreElements()) {

            Timestamp t = (Timestamp)hashTimes.nextElement();
            long diff3 = t1970.getTime()-t.getTime();
            long diff4 = -diff3;

            //System.out.println("TimeVariantSurvivalModel.getBoundingModels():");
            //System.out.println("diff3 = " + diff3);
            //System.out.println("diff4 = " + diff4);
            //System.out.println("t = " + t.toString());

            if(diff3 <= diff1 && diff3 >= 0) {

                diff1 = diff3;
                t1 = t;

            } //if(diff3 < diff1 && diff3 > 0)

            if(diff4 <= diff2 && diff4 >= 0) {

                diff2 = diff4;
                t2 = t;

            } //if(diff4 < diff2 && diff4 > 0)


        } //while(hashTimes.hasMoreElements())


        if(t1 == null && t2 != null)
            t1 = t2;
        else if(t1 != null && t2 == null)
            t2 = t1;

        if(t1 != null && t2!= null) {

            sm1 = (SurvivalModel)models.get(t1);
            sm2 = (SurvivalModel)models.get(t2);

            Timestamp boundingTime1 = switchYear(sourceTime,t1);
            Timestamp boundingTime2 = switchYear(sourceTime,t2);

            boundingModels.add(t1);
            boundingModels.add(t2);
            boundingModels.add(sm1);
            boundingModels.add(sm2);

            //System.out.println("boundingTime1 = " + boundingTime1.toString());
            //System.out.println("boundingTime2 = " + boundingTime2.toString());


        } //if(t1 != null && t2!= null)

        else {

            System.out.println("else, no bounding time found!");

        } //else

        return boundingModels;

    } //private Vector getBoundingModels(Timestamp t)








    public double getCriticalFloodingDepth(Timestamp t) {

        double criticalFD;
        Vector boundingModels = getBoundingModels(t);

        Timestamp t1 = (Timestamp) boundingModels.elementAt(0);
        Timestamp t2 = (Timestamp) boundingModels.elementAt(1);
        SurvivalModel sm1 = (SurvivalModel) boundingModels.elementAt(2);
        SurvivalModel sm2 = (SurvivalModel) boundingModels.elementAt(3);

        double y1 = sm1.getCriticalFloodingDepth(t1);
        double y2 = sm1.getCriticalFloodingDepth(t2);

        Timestamp t1970 = switchYear(1970,t);
        criticalFD = interpolate(t1,t2,y1,y2,t1970);

        return criticalFD;

    } //public double getCriticalFloodingDepth(Timestamp t)









    public double getProbability(long duration,double depth,Timestamp t) {

        double probability;
        Vector boundingModels = getBoundingModels(t);

        Timestamp t1 = (Timestamp) boundingModels.elementAt(0);
        Timestamp t2 = (Timestamp) boundingModels.elementAt(1);
        SurvivalModel sm1 = (SurvivalModel) boundingModels.elementAt(2);
        SurvivalModel sm2 = (SurvivalModel) boundingModels.elementAt(3);

        double y1 = sm1.getProbability(duration,depth,t1);
        double y2 = sm2.getProbability(duration,depth,t2);

        Timestamp t1970 = switchYear(1970,t);
        probability = interpolate(t1,t2,y1,y2,t1970);


        return probability;

    } //public double getProbability(double duration,double depth,Timestamp t)





    private double interpolate(Timestamp t1,Timestamp t2,double y1,double y2,Timestamp t) {

        double y;

        long x1 = t1.getTime();
        long x2 = t2.getTime();
        long x = t.getTime();

        if(x1 == x2) {

            y = y1;

        } //if(x1 == x2)

        else {

            double tt1 = (double)(x-x1);
            double tt2 = (double)(x2-x1);
            y = (y2-y1)*tt1/tt2+y1;

        } //else


        return y;

    } //private double interpolate(Timestamp t1,Timestamp t2,double y1,double y2,Timestamp t)





    private JComboBox getMonthCombo() {

        JComboBox jc = new JComboBox();

        for(int i=1;i<=12;i++) {

            String s = String.valueOf(i);
            jc.addItem(s);

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

        return jc;

    } //private JComboBox getMonthCombo()








    private JComboBox getDayCombo() {

        JComboBox jc = new JComboBox();

        for(int i=1;i<32;i++) {

            String s = String.valueOf(i);
            jc.addItem(s);

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

        return jc;

    } //private JComboBox getDayCombo()







    private Vector getModels() {

        Vector rows = new Vector();
        Enumeration enum = timestamps.keys();

        while(enum.hasMoreElements()) {

            String modelName = (String)enum.nextElement();
            Timestamp t = (Timestamp)timestamps.get(modelName);

            Calendar c = Calendar.getInstance();
            c.setTime(t);
            Integer month = new Integer(c.get(Calendar.MONTH)+1);
            Integer day = new Integer(c.get(Calendar.DATE));

            Vector oneRow = new Vector();
            oneRow.add(month);
            oneRow.add(day);
            oneRow.add(modelName);

            rows.add(oneRow);

        } //while(enum.hasMoreElements())

        return rows;

    } //private Vector getModels()








    public DisplayFrame showDetails(boolean editable) {

        editable = true;

        boolean ALLOW_ROW_SELECTION = true;

        Vector columnNames = new Vector();
        columnNames.add("Month");
        columnNames.add("Day");
        columnNames.add("Name");

        Vector row = getModels();

        GeneralTableModel tm = new GeneralTableModel(row,columnNames);
        tm.setEditableColumn(0);
        tm.setEditableColumn(1);

        table = new JTable(tm);
        table.setPreferredScrollableViewportSize(new Dimension(200,100));


        TableColumn monthColumn = table.getColumnModel().getColumn(0);
        monthColumn.setCellEditor(new DefaultCellEditor(getMonthCombo()));

        TableColumn dayColumn = table.getColumnModel().getColumn(1);
        dayColumn.setCellEditor(new DefaultCellEditor(getDayCombo()));

        tableScrollPane = new JScrollPane(table,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        JPanel tablePanel = new JPanel();
        tablePanel.setBorder(BorderFactory.createTitledBorder("Defined SurvivalModels"));
        tablePanel.add(tableScrollPane);

        modelsCombo = new JComboBox(
                            ConfigData.readURLProperties(IDSSAppConstants.configDir,
                                                IDSSAppConstants.smModelsFile,this.getClass()));

                    //ConfigData.readProperties(
                            //IDSSAppConstants.configDir,IDSSAppConstants.smModelsFile));


        defineButton = new JButton(IDSSAppConstants.defineButtonText);
        detailsButton = new JButton(IDSSAppConstants.detailsButtonText);
        disposeButton = new JButton(IDSSAppConstants.disposeButtonText);
        acceptButton = new JButton(IDSSAppConstants.acceptButtonText);

        defineButton.setEnabled(editable);
        detailsButton.setEnabled(false);
        disposeButton.setEnabled(false);
        acceptButton.setEnabled(editable);

        defineButton.setActionCommand(IDSSAppConstants.defineButtonCommand);
        detailsButton.setActionCommand(IDSSAppConstants.detailsButtonCommand);
        disposeButton.setActionCommand(IDSSAppConstants.disposeButtonCommand);
        acceptButton.setActionCommand(IDSSAppConstants.acceptButtonCommand);

        defineButton.addActionListener(this);
        detailsButton.addActionListener(this);
        disposeButton.addActionListener(this);
        acceptButton.addActionListener(this);


        userSMButton = new JButton(IDSSAppConstants.getUserSMButtonText);
        userSMButton.setActionCommand(IDSSAppConstants.getUserSMButtonCommand);
        userSMButton.addActionListener(this);


        int hGap = 5;

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.X_AXIS));
        buttonPanel.add(modelsCombo);
        buttonPanel.add(defineButton);

        JPanel buttonPanel2 = new JPanel();
        buttonPanel2.setLayout(new BoxLayout(buttonPanel2,BoxLayout.X_AXIS));

        buttonPanel2.add(userSMButton);
        buttonPanel2.add(detailsButton);
        buttonPanel2.add(disposeButton);
        buttonPanel2.add(acceptButton);

        internalFrame = new DisplayFrame(modelName,false,true,false,true,null);
        internalFrame.setBackground(Color.lightGray);

        internalFrame.getContentPane().setLayout(new BoxLayout(internalFrame.getContentPane(),BoxLayout.Y_AXIS));

        internalFrame.getContentPane().add(tablePanel);
        internalFrame.getContentPane().add(buttonPanel);
        internalFrame.getContentPane().add(buttonPanel2);

        internalFrame.pack();
        internalFrame.setVisible(true);





        if (ALLOW_ROW_SELECTION) { // true by default
            ListSelectionModel rowSM = table.getSelectionModel();
            rowSM.addListSelectionListener(new ListSelectionListener() {
                public void valueChanged(ListSelectionEvent e) {
                    //Ignore extra messages.
                    if (e.getValueIsAdjusting()) return;
                
                    ListSelectionModel lsm = (ListSelectionModel)e.getSource();
                    if (lsm.isSelectionEmpty()) {
                        System.out.println("No rows are selected.");

                        detailsButton.setEnabled(false);
                        disposeButton.setEnabled(false);

                    } else {

                        int selectedRow = lsm.getMinSelectionIndex();
                        detailsButton.setEnabled(true);
                        disposeButton.setEnabled(true);

                        currentMonth = Integer.parseInt(table.getValueAt(selectedRow,0).toString())-1;
                        currentDay = Integer.parseInt(table.getValueAt(selectedRow,1).toString());
                        currentModel = (String)table.getValueAt(selectedRow,2);

                        System.out.println(currentModel);


                    } //else
                } //public void valueChanged(ListSelectionEvent e)
            });
        } else {
            table.setRowSelectionAllowed(false);
        }


        return internalFrame;


    } //public DisplayFrame showDetails(boolean editable)








    private void addToTable(Vector row) {

        GeneralTableModel tm = (GeneralTableModel) table.getModel();

        tm.addRow(row);
        table.setModel(tm);

        int newRowNum = table.getRowCount()-1;
        table.setRowSelectionInterval(newRowNum,newRowNum);

        JViewport vp = tableScrollPane.getViewport();
        Point p = vp.getViewPosition();
        int rowHeight = table.getRowHeight();
        Point newPoint = new Point(p.x,p.y+rowHeight);
        vp.setViewPosition(newPoint);
        tableScrollPane.setViewport(vp);

    } //private void addToTable(Vector row)






    private void removeFromTable(Vector row) {

        GeneralTableModel tm = (GeneralTableModel) table.getModel();

        tm.removeRow(row);
        table.setModel(tm);

    } //private void removeFromTable(Vector row)










    public void setCriticalFloodingDepth(double d) {}


    public double getProbability(double d1,double d2) {return 0;}









    public void actionPerformed(ActionEvent ae) {


        String command = ae.getActionCommand();

        if(command.equals(IDSSAppConstants.defineButtonCommand)) {

            defineAModel();

        } //if(command.equals(IDSSAppConstants.defineButtonCommand))

        else if(command.equals(IDSSAppConstants.detailsButtonCommand)) {

            viewModelDetails();

        } //if(command.equals(IDSSAppConstants.detailsButtonCommand))


        else if(command.equals(IDSSAppConstants.disposeButtonCommand)) {

            removeModel();

        } //if(command.equals(IDSSAppConstants.disposeButtonCommand))


        else if(command.equals(IDSSAppConstants.acceptButtonCommand)) {


            acceptModel();


        } //if(command.equals(IDSSAppConstants.acceptButtonCommand))



        else if (ae.getActionCommand().equals(IDSSAppConstants.getUserSMButtonCommand)) {

            getUserSM();

        } //else if (ae.getActionCommand().equals(IDSSAppConstants.getUserSMButtonCommand))



    } //public void actionPerformed(ActionEvent ae)







    private void getUserSM() {

            File userSMFile = FileNameDialog.getInputFileName("Select User SM File",null,null);
            Vector userSMVector = new Vector();

            if(userSMFile != null) {

                userSMVector = ConfigData.readProperties(userSMFile);

                if(userSMVector != null) {

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

                        String s = (String)userSMVector.elementAt(i);
                        modelsCombo.addItem(s);

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

                    String smPath = ".";
                    File parentFile = userSMFile.getParentFile();

                    if(parentFile != null)
                        smPath = parentFile.toString();

                    System.out.println("TimeVariantSurvivalModel.getUserSM(), smPath: " + smPath);
                    ConfigData.addToClassPath(smPath);

                } //if(dsnNamesVector != null)

            } //if(dsnFile != null)


    } //privare void getUserSM()








    private void acceptModel() {

            GeneralTableModel tm = (GeneralTableModel) table.getModel();
            Vector editedRows = tm.getRowsVector();

            Hashtable models2 = new Hashtable();

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

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

                int month = Integer.parseInt(oneRow.elementAt(0).toString())-1;
                int day = Integer.parseInt(oneRow.elementAt(1).toString());
                String modelName = (String)(oneRow.elementAt(2));

                Calendar c = Calendar.getInstance();
                c.set(1970,month,day,0,0,0);
                Timestamp timestamp2 = new Timestamp(c.getTime().getTime());

                Timestamp timestamp1 = (Timestamp) timestamps.get(modelName);
                SurvivalModel sm = (SurvivalModel) models.get(timestamp1);

                models2.put(timestamp2,sm);
                timestamps.remove(modelName);
                timestamps.put(modelName,timestamp2);

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

            models = models2;

            pcs.firePropertyChange(IDSSAppConstants.setSurvivalModel,null,getCopy());
            pcs.firePropertyChange(IDSSAppConstants.removeFromModelView,null,internalFrame);


    } //private void acceptModel()















    public SurvivalModel getCopy() {

        Hashtable models2 = new Hashtable();
        Enumeration enum = models.keys();

        while(enum.hasMoreElements()) {

            Timestamp hashTimestamp = (Timestamp) enum.nextElement();
            SurvivalModel sm = (SurvivalModel) models.get(hashTimestamp);
            SurvivalModel sm2 = sm.getCopy();
            models2.put(hashTimestamp,sm2);

        } //while(enum.hasMoreElements())

        Hashtable ts = new Hashtable();
        Enumeration enum2 = timestamps.keys();

        while(enum2.hasMoreElements()) {

            String hashString = (String) enum2.nextElement();
            Timestamp t = (Timestamp) timestamps.get(hashString);
            ts.put(hashString,t);

        } //while(enum2.hasMoreElements())


        TimeVariantSurvivalModel sm3 = new TimeVariantSurvivalModel();
        sm3.setModels(models2);
        sm3.setTimestamps(ts);
        sm3.setModelCalendar(modelCalendar);

        return sm3;

    } //private Hashtable getModels()





    public void propertyChange(PropertyChangeEvent evt) {

        if (evt.getPropertyName().equals(IDSSAppConstants.setSurvivalModel)) {

            SurvivalModel sm = (SurvivalModel)evt.getNewValue();

            if(sm != null || sm instanceof SurvivalModel) {

                setSurvivalModel(sm);


            } //if(sm != null || sm instanceof SurvivalModel)

            else {

                JOptionPane.showMessageDialog(null,"x,Failed to receive Survivalodel",
                                        "Error",JOptionPane.ERROR_MESSAGE);
                
            } //else

        } //if (evt.getPropertyName().equals(IDSSAppConstants.setSurvivalModel))



        else if (evt.getPropertyName().equals(IDSSAppConstants.removeFromModelView)) {

            pcs.firePropertyChange(evt.getPropertyName(),evt.getOldValue(),evt.getNewValue());

        } //else if (evt.getPropertyName().equals(IDSSAppConstants.removeFromModelView))


    } //public void propertyChange(PropertyChangeEvent evt)








    private void setSurvivalModel(SurvivalModel sm) {

        Vector row = new Vector();

        int month = modelCalendar.get(Calendar.MONTH)+1;
        int day = modelCalendar.get(Calendar.DATE);

        row.add(new Integer(month));
        row.add(new Integer(day));
        String nameColumn = sm.getName()+":"+String.valueOf(month)+"/"+String.valueOf(day);
        row.add(nameColumn);
        addToTable(row);

        Timestamp hashTimestamp = new Timestamp(modelCalendar.getTime().getTime());

        models.put(hashTimestamp,sm);
        timestamps.put(nameColumn,hashTimestamp);

        JOptionPane.showMessageDialog(null,"x,Successfully received Survivalodel for \n" +
                                nameColumn,
                                "Information",JOptionPane.INFORMATION_MESSAGE);

        modelCalendar.set(Calendar.DATE,++day);


    } //private void setSurvivalModel(SurvivalModel sm)













    private void defineAModel() {



            try {

                String currentModel = (String) modelsCombo.getSelectedItem();
                SurvivalModel sm = (SurvivalModel)Class.forName(currentModel).newInstance();

                sm.addPropertyChangeListener(this);
                pcs.firePropertyChange(IDSSAppConstants.addToModelView,null,sm.showDetails(true));


            } //try


            catch(ClassNotFoundException e) {

                JOptionPane.showMessageDialog(null,"ClassNotFoundException\n" + e.toString(),
                                        "Error",JOptionPane.ERROR_MESSAGE);

            } //catch(ClassNotFoundException e)


            catch(InstantiationException e) {

                JOptionPane.showMessageDialog(null,"InstantiationException\n" + e.toString(),
                                        "Error",JOptionPane.ERROR_MESSAGE);

            } //catch(InstantiationException e)


            catch(IllegalAccessException e) {

                JOptionPane.showMessageDialog(null,"IllegalAccessException\n" + e.toString(),
                                        "Error",JOptionPane.ERROR_MESSAGE);

            } //catch(IllegalAccessException e)

    } //private void defineAModel()






    private void viewModelDetails() {


        //String hashString = String.valueOf(currentMonth+1)+"/"+String.valueOf(currentDay);
        System.out.println("currentModel : " + currentModel);
        Timestamp hashTimestamp = (Timestamp)timestamps.get(currentModel);

        SurvivalModel sm = (SurvivalModel) models.get(hashTimestamp);

        if(sm != null)
            pcs.firePropertyChange(IDSSAppConstants.addToModelView,null,sm.showDetails(false));

        else {
            JOptionPane.showMessageDialog(null,"No model defined for " + currentMonth + "/" +
                                    currentDay,"Information",JOptionPane.INFORMATION_MESSAGE);

            Enumeration enum = models.keys();

            System.out.println("TimeVariantSurvivalModel.viewModelDetails(), list of models hashtable:");

            while(enum.hasMoreElements()) {

                Timestamp t = (Timestamp) enum.nextElement();
                SurvivalModel sm2 = (SurvivalModel) models.get(t);
                System.out.println(t.toString() + " : " + sm2.getName());

            } //while(enum.hasMoreElements())

            System.out.println("Your timestamp is " + hashTimestamp.toString());

        } //else

    } //private void viewModelDetails()





    private void removeModel() {


        System.out.println("currentModel : " + currentModel);

        Timestamp hashTimestamp = (Timestamp)timestamps.get(currentModel);
        System.out.println("hashTimestamp : " + hashTimestamp.toString());

        if(hashTimestamp != null) {
            SurvivalModel sm = (SurvivalModel) models.get(hashTimestamp);

            if(sm != null) {

                Vector row = new Vector();
                row.add(new Integer(currentMonth+1));
                row.add(new Integer(currentDay));
                row.add(currentModel);

                removeFromTable(row);
                models.remove(hashTimestamp);
                timestamps.remove(currentModel);

            } //if(sm != null)

            else
                System.out.println("sm = null");

        } //if(hashTimestamp != null)
        else {

            System.out.println("hashTimestamp = null");
            Enumeration enum = timestamps.keys();
            System.out.println("timestamps listing:");

            while(enum.hasMoreElements()) {

                String s = (String) enum.nextElement();
                Timestamp t = (Timestamp) timestamps.get(s);

                System.out.println(s + " : " + t.toString());

            } //while(enum.hasMoreElements())

        } //else

    } //private void removeModel()






    public Hashtable getParameters() {

        Hashtable h = new Hashtable();

        return h;

    } //public Hashtable getParameters()







    public void setParameters(Hashtable h) {}





} //public class TimeVariantSurvivalModel


