package org.openstreetmap.josm.plugins.UnitScale;

import static org.openstreetmap.josm.tools.I18n.tr;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.GBC;
import org.openstreetmap.josm.data.SystemOfMeasurement;
import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;

import java.util.List;
import java.util.ArrayList;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JComboBox;
import javax.swing.JCheckBox;
import javax.swing.JTextField;
import javax.swing.ButtonGroup;
import javax.swing.BoxLayout;
import javax.swing.Box;

public class UnitScalePreferences extends DefaultTabPreferenceSetting implements ActionListener {
    private static class UnitPanel extends JPanel {
        private String label;
        private String equivalentUnit;
        private boolean optional;
        
        private JLabel labelLabel;
        private JTextField unitNameField;
        private JTextField equivalentAmountField;
        private JLabel equivalentUnitLabel;
        private JCheckBox specifiedCheck;
        
        public UnitPanel(String label, String equivalentUnit) {
            this(label, equivalentUnit, false);
        }
        
        public UnitPanel(String label, String equivalentUnit, boolean optional) {
            this.label = label;
            this.equivalentUnit = equivalentUnit;
            this.optional = optional;
            createPanel();
        }
        
        private void createPanel() {
            if (optional) {
                specifiedCheck = new JCheckBox();
                add(specifiedCheck);
            }

            labelLabel = new JLabel(label);
            add(labelLabel);
            
            unitNameField = new JTextField("", 6);
            unitNameField.setMinimumSize(new Dimension(100, unitNameField.getHeight()));
            add(unitNameField);
            
            equivalentAmountField = new JTextField("", 6);
            equivalentAmountField.setMinimumSize(new Dimension(100, equivalentAmountField.getHeight()));
            add(equivalentAmountField);
            
            equivalentUnitLabel = new JLabel(equivalentUnit);
            add(equivalentUnitLabel);
        }
        
        public void setLabel(String label) {
            this.label = label;
            labelLabel.setText(label);
        }
        
        public void setEquivalentUnit(String name) {
            equivalentUnit = name;
            equivalentUnitLabel.setText(name);
        }
        
        public void setEquivalentAmount(double eq) {
            equivalentAmountField.setText(Double.toString(eq));
        }
        
        public String getUnitName() {
            return unitNameField.getText();
        }
        
        public void setUnitName(String name) {
            unitNameField.setText(name);
        }
        
        public boolean isSpecified() {
            if (!optional) {
                return true;
            } else {
                return specifiedCheck.isSelected();
            }
        }
        
        public void setSpecified(boolean spec) {
            if (optional) {
                specifiedCheck.setSelected(spec);
            }
        }
        
        public double getUnitEquivalence() {
            double eq;
            try {
                eq = Double.parseDouble(equivalentAmountField.getText());
            } catch (NumberFormatException e) {
                eq = 1;
            }
            return eq;
        }
    }
    
    private JPanel panel = new JPanel();
    
    private UnitPanel length;
    private UnitPanel lengthLong;
    private UnitPanel speed;
    private UnitPanel area;
    
    private JTextField unitNameField;
    private JComboBox unitComboBox;
    private JComboBox equivalentUnitComboBox;
    
    private SystemOfMeasurement internalSoM = new SystemOfMeasurement(
                "internal",
                1, "m",
                1, "m",
                "m/s^-1", 1,
                1, "m^2");
    
    private SystemOfMeasurement lastSoM = internalSoM;
    private SystemOfMeasurement referenceSoM = internalSoM;
    
    public UnitScalePreferences() {
        super(null, tr("Custom units"), tr("Add and edit custom units."));
        createPanel();
    }
    
    public String[] getAllUnits() {
        return getAllUnits(false);
    }
    
    public String[] getAllUnits(boolean addNew) {
        return getAllUnits(addNew, false);
    }
    
    public String[] getAllUnits(boolean addNew, boolean addInternal) {
        List<String> out = new ArrayList<String>();
        if (addInternal) {
            out.add(tr("JOSM internal"));
        }
        out.addAll(SystemOfMeasurement.ALL_SYSTEMS.keySet());
        if (addNew) {
            out.add(tr("New unit"));
        }
        return out.toArray(new String[0]);
    }

    private void createPanel() {
        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
        
        String[] units = getAllUnits(true);
    
        JPanel unitSelect = new JPanel();
        JLabel unitLabel = new JLabel(tr("System:"));
        unitSelect.add(unitLabel);
        unitComboBox = new JComboBox<String>(units);
        unitComboBox.setActionCommand("unitSelect");
        unitComboBox.addActionListener(this);
        unitSelect.add(unitComboBox);
        panel.add(unitSelect);

        JPanel unitName = new JPanel();
        JLabel unitNameLabel = new JLabel(tr("Name:"));
        unitName.add(unitNameLabel);
        unitNameField = new JTextField("", 20);
        unitName.add(unitNameField);
        panel.add(unitName);
    
        JPanel equivalentUnit = new JPanel();
        JLabel equivalentUnitLabel = new JLabel(tr("In terms of:"));
        equivalentUnit.add(equivalentUnitLabel);
        equivalentUnitComboBox = new JComboBox<String>(getAllUnits(false, true));
        equivalentUnitComboBox.setActionCommand("equivalentUnit");
        equivalentUnitComboBox.addActionListener(this);
        equivalentUnit.add(equivalentUnitComboBox);
        panel.add(equivalentUnit);


        length = new UnitPanel(tr("Length:"), "m");
        panel.add(length);

        lengthLong = new UnitPanel(tr("Length (long):"), "m");
        panel.add(lengthLong);

        speed = new UnitPanel(tr("Speed:"), "m/s^-1");
        panel.add(speed);

        area = new UnitPanel(tr("Area:"), "m^2", true);
        panel.add(area);

        JButton delete = new JButton(tr("Delete system"));
        delete.setActionCommand("deleteUnit");
        delete.addActionListener(this);
        panel.add(delete);

        panel.add(Box.createVerticalGlue());
        
        updateUnit(units[0]);
    }
    
    public void updateUnit(String unit) {
        SystemOfMeasurement som = UnitScaleCommon.allSystemGet(unit);
        if (som == null) {
            som = new SystemOfMeasurement(
                "",
                1, "",
                1, "",
                "", 1,
                1, "");
        }
        unitNameField.setText(som.getName());
        
        length.setUnitName(som.aName);
        length.setEquivalentAmount(som.aValue);
        
        lengthLong.setUnitName(som.bName);
        lengthLong.setEquivalentAmount(som.bValue);
        
        speed.setUnitName(som.speedName);
        speed.setEquivalentAmount(som.speedValue);
        
        if (som.areaCustomName != null && som.areaCustomValue != -1) {
            area.setSpecified(true);
            area.setUnitName(som.areaCustomName);
            area.setEquivalentAmount(som.areaCustomValue);
        } else {
            area.setSpecified(false);
            area.setUnitName("");
            area.setEquivalentAmount(0);
        }
        lastSoM = internalSoM;
        scaleUnits();
    }
    
    public void changeRelative(String relative) {
        lastSoM = referenceSoM;
        referenceSoM = UnitScaleCommon.allSystemGet(relative);
        if (referenceSoM == null) {
            referenceSoM = internalSoM;
        }

        length.setEquivalentUnit(referenceSoM.aName);
        lengthLong.setEquivalentUnit(referenceSoM.bName);
        speed.setEquivalentUnit(referenceSoM.speedName);
        area.setEquivalentUnit(referenceSoM.areaCustomName);
    }
    
    public void scaleUnits() {
        length.setEquivalentAmount(
            (length.getUnitEquivalence() * lastSoM.aValue)
            / referenceSoM.aValue);
        lengthLong.setEquivalentAmount(
            (lengthLong.getUnitEquivalence() * lastSoM.bValue)
            / referenceSoM.bValue);
        speed.setEquivalentAmount(
            (speed.getUnitEquivalence() * lastSoM.speedValue)
            / referenceSoM.speedValue);
        area.setEquivalentAmount(
            (area.getUnitEquivalence() * lastSoM.areaCustomValue)
            / referenceSoM.areaCustomValue);
    }
    
    public void deleteUnit(String unit) {
        UnitScaleUnitManager.remove(unit);
        UnitScaleUnitManager.save();
        panel.removeAll();
        createPanel();
    }

    @Override
    public void addGui(PreferenceTabbedPane gui) {
        JPanel tab = gui.createPreferenceTab(this);
        tab.add(panel, GBC.eol().fill(GBC.BOTH));
    }
    
    @Override
    public boolean ok() {
        SystemOfMeasurement som;
        if (area.isSpecified()) {
            som = new SystemOfMeasurement(
                unitNameField.getText(),
                length.getUnitEquivalence() * referenceSoM.aValue, length.getUnitName(),
                lengthLong.getUnitEquivalence() * referenceSoM.bValue, lengthLong.getUnitName(),
                speed.getUnitName(), speed.getUnitEquivalence() * referenceSoM.speedValue,
                area.getUnitEquivalence() * referenceSoM.areaCustomValue, area.getUnitName());
        } else {
            som = new SystemOfMeasurement(
                unitNameField.getText(),
                length.getUnitEquivalence() * referenceSoM.aValue, length.getUnitName(),
                lengthLong.getUnitEquivalence() * referenceSoM.bValue, lengthLong.getUnitName(),
                speed.getUnitName(), speed.getUnitEquivalence() * referenceSoM.speedValue);
        }
        UnitScaleUnitManager.put(som);
        UnitScaleUnitManager.save();
        return false;
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        String selected = null;
        if (e.getSource() instanceof JComboBox) {
            JComboBox cb = (JComboBox)e.getSource();
            selected = (String)cb.getSelectedItem();
        }

        switch (e.getActionCommand()) {
            case "unitSelect":
                updateUnit(selected);
                break;
            case "equivalentUnit":
                changeRelative(selected);
                scaleUnits();
                break;
            case "deleteUnit":
                deleteUnit((String)unitComboBox.getSelectedItem());
                break;
        }
    }
}

