/*
 * FieldDataModel.java
 *
 * Created on February 6, 2007, 12:36 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package usda.weru.erosion.barriereditor;

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import com.klg.jclass.table.EditableTableDataModel;
import com.klg.jclass.table.data.AbstractDataSource;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import de.schlichtherle.truezip.file.TFile;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.swing.DefaultComboBoxModel;
import javax.swing.SwingWorker;
import usda.weru.erosion.DataStore;
import usda.weru.util.ConfigData;

/**
 *
 * @author Joseph Levin
 */
public class FieldDataModel extends AbstractDataSource implements EditableTableDataModel, ListEventListener<BarrierPolygon> {

    private static final long serialVersionUID = 1L;

    /**
     *
     */
    protected PropertyChangeSupport c_changes;

    /**
     *
     */
    protected EventList<BarrierPolygon> c_barriers;

    /**
     *
     */
    protected Map<String, BarrierPolygon> c_barrierMap;

    /**
     *
     */
    protected double c_fieldAngle;

    /**
     * Area of a field
     */
    //protected double c_fieldArea;
    
    /**
     *
     */
    protected FieldPolygon c_field;

    /**
     *
     */
    protected BarrierTypeModel<?> c_typeModel;

    /**
     * Creates a new instance of FieldDataModel
     */
    public FieldDataModel() {
        c_typeModel = new BarrierTypeModel<>();
        c_field = new FieldPolygon();
        c_changes = new PropertyChangeSupport(this);
        c_barriers = new BasicEventList<BarrierPolygon>();
        c_barriers.addListEventListener(this);
        c_barrierMap = new Hashtable<String, BarrierPolygon>();
    }

    /**
     *
     * @return
     */
    public BarrierTypeModel<?> getBarrierTypeModel() {
        return c_typeModel;
    }

    /**
     *
     * @param l
     */
    public void addPropertyChangeListener(PropertyChangeListener l) {
        c_changes.addPropertyChangeListener(l);
    }

    /**
     *
     * @param l
     */
    public void removePropertyChangeListener(PropertyChangeListener l) {
        c_changes.removePropertyChangeListener(l);
    }

    /**
     *
     * @return
     */
    public FieldPolygon getFieldPolygon() {
        return c_field;
    }

    /**
     *
     * @param field
     */
    public void setFieldPolygon(FieldPolygon field) {
        if (field == null) {
            return;
        }
        c_field = field;
        c_changes.firePropertyChange(DataStore.SimField, null, c_field.clone());
    }

    /**
     *
     * @return
     */
    public double getFieldLength() {
        return c_field.getLen();
    }

    /**
     *
     * @param length
     */
    public void setFieldLength(String length) {
        c_field.setLenStr(length);
        c_changes.firePropertyChange(DataStore.SimField, null, c_field.clone());
    }

    /**
     *
     * @return
     */
    public double getFieldWidth() {
        return c_field.getWid();

    }

    /**
     *
     * @param width
     */
    public void setFieldWidth(String width) {
        c_field.setWidStr(width);
        c_changes.firePropertyChange(DataStore.SimField, null, c_field.clone());
    }

    /**
     *
     * @return
     */
    public double getFieldAngle() {
        return c_fieldAngle;
    }

    /**
     *
     * @param angle
     */
    public void setFieldAngle(double angle) {
        double oldValue = c_fieldAngle;
        c_fieldAngle = angle;
        c_changes.firePropertyChange(DataStore.SimRegAngle, oldValue, c_fieldAngle);
    }

    /**
     *
     * @param text
     */
    public void setFieldAngle(String text) {
        double value = Double.parseDouble(text);
        setFieldAngle(value);
    }
    
//    /**
//     *
//     * @return
//     */
//    public double getFieldArea() {
//        return c_fieldArea;
//    }
//
//    /**
//     *
//     * @param area
//     */
//    public void setFieldArea(double area) {
//        double oldValue = c_fieldArea;
//        c_fieldArea = area;
//        c_changes.firePropertyChange(DataStore.SimRegAngle, oldValue, c_fieldArea);
//    }
//
//    /**
//     *
//     * @param text
//     */
//    public void setFieldArea(String text) {
//        double value = Double.parseDouble(text);
//        setFieldAngle(value);
//    }

    /** Creates a new instance of BarrierModel
     * @param name */
    public void addBarrier(String name) {
        if (name == null || name.length() == 0) {
            return;
        }

        if (containsBarrier(name)) {
            return;
        }

        BarrierPolygon temp = new BarrierPolygon(0, 0, 0, 0, 0, 0, 0, name, "");
        c_barrierMap.put(name, temp);
        c_barriers.add(temp);
        c_changes.firePropertyChange(DataStore.BarriersObj + "-" + name, name, temp.clone());
    }

    /**
     *
     * @param name
     * @param barrier
     */
    public void updateBarrier(String name, BarrierPolygon barrier) {
        if (!containsBarrier(name)) {
            addBarrier(name);
        }
        BarrierPolygon temp = getBarrier(name);
        temp.setType(barrier.getType());
        temp.setPorosity(barrier.getPorosity());
        temp.setHgt(barrier.getHgt());
        temp.setWid(barrier.getWid());
        temp.setX1(barrier.getX1());
        temp.setY1(barrier.getY1());
        temp.setX2(barrier.getX2());
        temp.setY2(barrier.getY2());

        c_changes.firePropertyChange(DataStore.BarriersObj + "-" + name, name, temp.clone());

    }

    /**
     *
     * @param name
     */
    public void removeBarrier(String name) {
        if (name == null || name.length() == 0) {
            return;
        }

        if (!containsBarrier(name)) {
            return;
        }

        BarrierPolygon temp = c_barrierMap.get(name);
        c_barrierMap.remove(name);
        c_barriers.remove(temp);
        temp = null;
        c_changes.firePropertyChange(DataStore.BarriersObj + "-" + name, null, null);
    }

    /**
     *
     * @param name
     * @return
     */
    public boolean containsBarrier(String name) {
        return c_barrierMap.containsKey(name);
    }

    /**
     *
     */
    public void clearBarriers() {
        for (int i = 0; i < getBarrierCount(); i++) {
            BarrierPolygon temp = getBarrier(i);
            removeBarrier(temp.getName());
        }
    }

    /**
     *
     * @return
     */
    public int getBarrierCount() {
        return c_barriers.size();
    }

    /**
     *
     * @param name
     * @return
     */
    public BarrierPolygon getBarrier(String name) {
        return c_barrierMap.get(name);
    }

    /**
     *
     * @param index
     * @return
     */
    public BarrierPolygon getBarrier(int index) {
        if (index < 0 || index >= getNumRows()) {
            return null;
        }
        return c_barriers.get(index);
    }

    @Override
    public int getNumRows() {
        return c_barriers.size();
    }

    @Override
    public int getNumColumns() {
        return 9;
    }

    @Override
    public Object getTableRowLabel(int row) {
        return null;
    }

    @Override
    public Object getTableColumnLabel(int column) {
        switch (column) {
            case 0:
                return "name";
            case 1:
                return "type";
            case 2:
                return "x1";
            case 3:
                return "y1";
            case 4:
                return "x2";
            case 5:
                return "y2";
            case 6:
                return "height";
            case 7:
                return "width";
            case 8:
                return "porosity";
            default:
                return null;
        }
    }

    @Override
    public Object getTableDataItem(int row, int column) {
        BarrierPolygon barrier = getBarrier(row);
        if (barrier == null) {
            return null;
        }
        switch (column) {
            case 0:
                return barrier.getName();
            case 1:
                return barrier.getType();
            case 2:
                return barrier.getX1();
            case 3:
                return barrier.getY1();
            case 4:
                return barrier.getX2();
            case 5:
                return barrier.getY2();
            case 6:
                return barrier.getHgt();
            case 7:
                return barrier.getWid();
            case 8:
                return barrier.getPorosity();
            default:
                return null;
        }
    }

    @Override
    public boolean setTableDataItem(Object o, int row, int column) {
        BarrierPolygon barrier = getBarrier(row);
        if (barrier == null || o == null) {
            return false;
        }
        String valueString = o.toString();
        double valueDouble = 0d;
        boolean validDouble = false;
        try {
            valueDouble = Double.parseDouble(valueString);
            validDouble = true;
        } catch (Exception e) {
            validDouble = false;
        }
        String name = getBarrier(row).getName();
        boolean changed = false;
        switch (column) {
            case 0:
                barrier.setName(valueString);
                changed = true;
                break;
            case 1:
                BarrierType type = c_typeModel.getType(valueString);
                if (type != null) {
                    barrier.setType(valueString);
                    try {
                        double width = Double.parseDouble(type.getWidth());
                        double height = Double.parseDouble(type.getHeight());
                        double porosity = Double.parseDouble(type.getPorosity());
                        barrier.setWid(width);
                        barrier.setHgt(height);
                        barrier.setPorosity(porosity);
                        changed = true;
                        //TODO: Fix the row firing code, Dirty Hack
                        fireDataReset();
                        //fireRowChanged(row);
                    } catch (NumberFormatException e) {
                        System.err.println(e);
                    }
                }
                break;
            case 2:
                if (!validDouble) {
                    return false;
                }
                barrier.setX1(valueDouble);
                changed = true;
                break;
            case 3:
                if (!validDouble) {
                    return false;
                }
                barrier.setY1(valueDouble);
                changed = true;
                break;
            case 4:
                if (!validDouble) {
                    return false;
                }
                barrier.setX2(valueDouble);
                changed = true;
                break;
            case 5:
                if (!validDouble) {
                    return false;
                }
                barrier.setY2(valueDouble);
                changed = true;
                break;
            case 6:
                if (!validDouble) {
                    return false;
                }
                barrier.setHgt(valueDouble);
                barrier.setType("<Custom>");
                //fireRowChanged(row);
                fireDataReset();
                changed = true;
                break;
            case 7:
                if (!validDouble) {
                    return false;
                }
                barrier.setWid(valueDouble);
                barrier.setType("<Custom>");
                //fireRowChanged(row);
                fireDataReset();
                changed = true;
                break;
            case 8:
                if (!validDouble) {
                    return false;
                }
                barrier.setPorosity(valueDouble);
                barrier.setType("<Custom>");
                //fireRowChanged(row);
                fireDataReset();
                changed = true;
                break;

            default:
                return false;
        }
        if (changed) {
            c_changes.firePropertyChange(DataStore.BarriersObj + "-" + name, null, getBarrier(row));
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @param listChanges
     */
    @Override
    public void listChanged(ListEvent<BarrierPolygon> listChanges) {
        // for all changes, one block at a time
        while (listChanges.nextBlock()) {
            // get the current change info
            int startIndex = listChanges.getBlockStartIndex();
            int endIndex = listChanges.getBlockEndIndex();
            int count = endIndex - startIndex + 1;
            int changeType = listChanges.getType();

            switch (changeType) {
                case ListEvent.INSERT:
                    fireRowsAdded(startIndex, count);
                    break;
                case ListEvent.DELETE:
                    fireRowDeleted(startIndex, count);
                    break;
                case ListEvent.UPDATE:
                    for (int i = startIndex; i <= endIndex; i++) {
                        fireRowChanged(i);
                    }
                    break;
            }

        }
    }

    /**
     *
     * @param <E>
     */
    public static class BarrierTypeModel<E> extends DefaultComboBoxModel<BarrierType> {

        private static final long serialVersionUID = 1L;

        /**
         *
         */
        protected List<BarrierType> c_templates;

        /**
         *
         */
        protected final BarrierType c_customType = new BarrierType("<Custom>");
        Object selected;

        /**
         *
         */
        public BarrierTypeModel() {
            ConfigData.getDefault().addPropertyChangeListener(ConfigData.BarriersFile, new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    load();
                }
            });
        }

        /**
         *
         */
        public void load() {

            SwingWorker<List<BarrierType>, BarrierType> worker = new SwingWorker<List<BarrierType>, BarrierType>() {

                @Override
                protected List<BarrierType> doInBackground() throws Exception {
                    String path = ConfigData.getDefault().getDataParsed(ConfigData.BarriersFile);
                    TFile file = path != null ? new TFile(path) : null;
                    List<BarrierType> templates = BarrierType.readFile(file);
                    templates.add(0, c_customType);
                    return templates;
                }

                @Override
                protected void done() {
                    try {
                        c_templates = get();
                    } catch (InterruptedException | ExecutionException e) {
                        c_templates = Collections.emptyList();
                    }
                    Object selected = getSelectedItem();
                    removeAllElements();
                    for (BarrierType type : c_templates) {
                        addElement(type);
                    }
                    setSelectedItem(selected);
                }
            };

            //start the swing worker.
            worker.execute();

        }

        /**
         *
         * @param name
         * @return
         */
        public BarrierType getType(String name) {
            if (name == null) {
                return null;
            }
            name = name.trim();
            for (BarrierType type : c_templates) {
                if (type.getName().equals(name) && type != c_customType) {
                    return type;
                }
            }
            return null;
        }
    }
}