/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/10/06, 17:03
!  AUTHOR(S): KOGA, Junichiro
!  File : AtomEditor.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.atomcoord;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import org.apache.log4j.Logger;

import ciss.phase_viewer.common.TaggedString;
import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableColumns;
import ciss.phase_viewer.mainpanel.InternalFrameChase;

/**
 * 原子一つの属性を編集するためのパネルを提供するクラス. 編集項目・編集方法などは,
 * InputInterfaceTableColumnsオブジェクトから取得する.
 * 
 * @author KOGA, Junichiro
 */
public class AtomEditor extends InternalFrameChase {
    private Logger logger = Logger.getLogger(AtomEditor.class.getName());

    protected InputInterfaceTableColumns[] columns;

    private Vector atomListeners;

    protected Atom atom;
    protected int atomIndex;
    protected boolean newAtom = false;

    private String element = "element";
    private String rx = "rx";
    private String ry = "ry";
    private String rz = "rz";

    /**
     * Creates a new instance of AtomEditor
     * 
     * @param columns
     *            InputInterfaceTableColumnsの配列. この配列によって編集する
     *            原子の属性の情報などを取得(たとえば, どんなエディターを使うか, など).
     * @param title
     *            パネルのタイトル.
     * @param dimesion
     *            起動時のパネルの大きさ.
     */
    public AtomEditor(InputInterfaceTableColumns[] columns, String title,
            Dimension dimension) {
        super(title, dimension);
        this.columns = columns;
        this.atomIndex = -1;
        init();
        setVisible(true);
    }

    /**
     * すでに存在するデータを編集し, かつそれを新原子として登録したい場合これ.
     * 
     * @param columns
     *            InputInterfaceTableColumnsの配列. この配列によって編集する
     *            原子の属性の情報などを取得(たとえば, どんなエディターを使うか, など).
     * @param atom
     *            編集したいAtomオブジェクト
     * @param newAtom
     *            真の場合, 既存の原子を編集するのではなく新たに作る.
     * @param atomIndex
     *            編集したいAtomオブジェクトのインデックス.
     * @param title
     *            パネルのタイトル.
     * @param dimesion
     *            起動時のパネルの大きさ.
     */
    public AtomEditor(InputInterfaceTableColumns[] columns, Atom atom,
            boolean newAtom, int atomIndex, String title, Dimension dimension) {
        super(title, dimension);
        this.columns = columns;
        this.atom = atom;
        this.atomIndex = atomIndex;
        this.newAtom = newAtom;
        init();
        setVisible(true);
    }

    /**
     * このパネルの"OK"ボタンをクリックすると対応する原子に変更が施される. そのことを通知されるリスナー, AtomListenerを登録する
     * 
     * @param atomListener
     *            AtomListenerインターフェースを実装したクラス.
     */
    public void addAtomListener(AtomListener atomListener) {
        if (atomListeners == null) {
            atomListeners = new Vector();
        }
        atomListeners.addElement(atomListener);
    }

    private void init() {
        Container container = getContentPane();
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
        JScrollPane scr = new JScrollPane();

        int compCount = 0;
        JPanel p = null;
        int ign = 0;
        for (int i = 0; i < columns.length; i++) {
            if (columns[i].ignoreOnSave()) {
                ign++;
            }
        }

        int foo = columns.length % 4;
        int dummy = 4 - foo + ign;
        if ((foo - ign) == 0) {
            dummy = 0;
        }

        for (int i = 0; i < columns.length + dummy; i++) {
            if (compCount == 0) {
                p = new JPanel();
                p.setLayout(new GridLayout(1, 4));
            }
            if (i < columns.length) {
                if (columns[i].ignoreOnSave()) {
                    continue;
                }
                logger.debug("creating gui for: " + columns[i].getName());
                p.add(createPanelFrom(columns[i]));
            } else {
                p.add(new JPanel());
            }
            compCount++;
            if (compCount == 4) {
                compCount = 0;
                container.add(p);
            }
        }

        JPanel btnpanel = new JPanel();
        btnpanel.setLayout(new BoxLayout(btnpanel, BoxLayout.X_AXIS));
        JButton btnok = new JButton("apply");
        JButton rmv = new JButton("remove selected");
        JButton btnclose = new JButton("close");
        btnpanel.add(btnok);
        btnpanel.add(rmv);
        btnpanel.add(btnclose);

        btnok.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doIt();
            }
        });

        rmv.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                removeSelected();
            }
        });

        btnclose.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });

        container.add(btnpanel);
    }

    protected void removeSelected() {
    }

    private Vector editors = new Vector();

    private JPanel createPanelFrom(InputInterfaceTableColumns column) {
        JPanel panel = new JPanel();
        panel.setBorder(new TitledBorder(column.getName()));
        AtomEditorEditors editor = AtomEditors.getInstance(column, atom, this);
        panel.add((JComponent) editor);
        panel.setToolTipText(column.getToolTip());
        editors.addElement(editor);
        return panel;
    }

    /**
     * 新しい原子をセット.
     * 
     * @param newAtom
     *            新原子
     */
    protected void setValue(Atom newAtom) {
        if (editors == null) {
            return;
        }
        for (int i = 0; i < editors.size(); i++) {
            AtomEditorEditors edi = (AtomEditorEditors) editors.elementAt(i);
            edi.removeListeners();
            AtomEditors.setValue(edi, edi.getIdentifiers(), newAtom);
            edi.restoreListeners();
            logger.debug("atom: " + newAtom);
        }
    }

    /**
     * GUIに入力された情報を利用して, 一気に原子を編集/新規作成. さらに登録されているリスナーに AtomEventを発行する.
     */
    protected void doIt() {
        if (atomListeners == null) {
            return;
        }

        String elem = "";
        String[] pos = new String[3];
        Vector auxvec = new Vector();

        for (int i = 0; i < editors.size(); i++) {
            AtomEditorEditors editor = (AtomEditorEditors) editors.elementAt(i);
            String ident = editor.getIdentifiers();
            if (ident.equals(element)) {
                elem = editor.getValue();
            } else if (ident.equals(rx)) {
                pos[0] = editor.getValue();
            } else if (ident.equals(ry)) {
                pos[1] = editor.getValue();
            } else if (ident.equals(rz)) {
                pos[2] = editor.getValue();
            } else {
                TaggedString aux = new TaggedString(ident, editor.getValue());
                auxvec.addElement(aux);
            }
        }

        atom = new Atom(elem, pos, auxvec);
        logger.debug("the atom object you've created : " + atom);

        int ind = atomIndex;
        if (newAtom) {
            ind = -1;
        }
        AtomEvent event = new AtomEvent(atom, ind);

        for (int i = 0; i < atomListeners.size(); i++) {
            ((AtomListener) atomListeners.elementAt(i)).atomStateChanged(event);
        }

        post();
    }

    /**
     * override this method in case you need some post-processing.
     */
    protected void post() {
    }

}

class AtomEditors {
    static AtomEditorEditors getInstance(InputInterfaceTableColumns column,
            Atom atom, AtomEditor parent) {
        String editor = column.getCellEditor();
        String ini = "";
        ini = initValue(column, atom);
        boolean bini = Utils.getBool(ini);

        if (editor.equals(InputInterfaceTableColumns.CHECK_BOX)) {
            return new AtomEditorCheckBox(column.getName(), bini, parent);
        } else if (editor.equals(InputInterfaceTableColumns.COMBO_BOX)) {
            return new AtomEditorComboBox(column.getName(), ini,
                    column.getChoices(), parent);
        } else if (editor.equals(InputInterfaceTableColumns.TEXT_FIELD)) {
            return new AtomEditorTextField(column.getName(), ini, parent,
                    column.getFormatter());
        }
        return null;
    }

    static void setValue(AtomEditorEditors editor, String name, Atom atom) {
        String ini = "";
        ini = getValueFrom(name, atom);
        editor.setValue(ini);
    }

    private static String initValue(InputInterfaceTableColumns column, Atom atom) {
        if (atom == null) {
            return column.getDefaultValue();
        }
        return getValueFrom(column.getName(), atom);
    }

    private static String getValueFrom(String name, Atom atom) {
        String element = "element";
        String rx = "rx";
        String ry = "ry";
        String rz = "rz";
        TaggedString[] au = atom.getAuxil();

        String ret = "";
        if (name.equals(element)) {
            ret = atom.getElementName();
        } else if (name.equals(rx)) {
            ret = atom.getPos()[0];
        } else if (name.equals(ry)) {
            ret = atom.getPos()[1];
        } else if (name.equals(rz)) {
            ret = atom.getPos()[2];
        } else {
            if (au != null) {
                for (int i = 0; i < au.length; i++) {
                    if (au[i].getTag() != null && au[i].getTag().equals(name)) {
                        ret = au[i].getValue();
                        break;
                    }
                }
            }
        }
        return ret;
    }

}

class AtomEditorCheckBox extends JCheckBox implements AtomEditorEditors {
    private String name;
    private AtomEditor parent;

    AtomEditorCheckBox(String name, boolean bini, AtomEditor parent) {
        super();
        setSelected(bini);
        this.name = name;
        this.parent = parent;
        addListener();
    }

    private ActionListener[] listeners;

    public void removeListeners() {
        listeners = getActionListeners();
        if (listeners == null) {
            return;
        }
        for (int i = 0; i < listeners.length; i++) {
            removeActionListener(listeners[i]);
        }
    }

    public void restoreListeners() {
        if (listeners == null) {
            return;
        }
        for (int i = 0; i < listeners.length; i++) {
            addActionListener(listeners[i]);
        }
    }

    private void addListener() {
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                parent.doIt();
            }
        });
    }

    public String getValue() {
        return isSelected() ? "1" : "0";
    }

    public String getIdentifiers() {
        return this.name;
    }

    public void setValue(String value) {
        boolean bval = Utils.getBool(value);
        setSelected(bval);
    }
}

class AtomEditorComboBox extends JComboBox implements AtomEditorEditors {
    private String name;
    private AtomEditor parent;

    AtomEditorComboBox(String name, String init, Object[] choices,
            AtomEditor parent) {
        super(choices);
        this.name = name;
        this.parent = parent;
        setSelectedItem(init);
        this.addListener();
    }

    private ActionListener[] listeners;

    public void removeListeners() {
        listeners = getActionListeners();
        if (listeners == null) {
            return;
        }
        for (int i = 0; i < listeners.length; i++) {
            removeActionListener(listeners[i]);
        }
    }

    public void restoreListeners() {
        if (listeners == null) {
            return;
        }
        for (int i = 0; i < listeners.length; i++) {
            addActionListener(listeners[i]);
        }
    }

    private void addListener() {
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                parent.doIt();
            }
        });
    }

    public String getValue() {
        Object foo = getSelectedItem();
        if (foo instanceof String) {
            return (String) foo;
        }
        return "";
    }

    public String getIdentifiers() {
        return this.name;
    }

    public void setValue(String value) {
        setSelectedItem(value);
    }
}

class AtomEditorTextField extends JTextField implements AtomEditorEditors {
    private String name;
    private AtomEditor parent;
    private java.text.DecimalFormat formater;

    AtomEditorTextField(String name, String init, AtomEditor parent,
            java.text.DecimalFormat formater) {
        super(init);
        setColumns(6);
        this.name = name;
        this.parent = parent;
        this.formater = formater;
        addListener();
    }

    private ActionListener[] listeners;

    public void removeListeners() {
        listeners = getActionListeners();
        if (listeners == null) {
            return;
        }
        for (int i = 0; i < listeners.length; i++) {
            removeActionListener(listeners[i]);
        }
    }

    public void restoreListeners() {
        if (listeners == null) {
            return;
        }
        for (int i = 0; i < listeners.length; i++) {
            addActionListener(listeners[i]);
        }
    }

    private void addListener() {
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                parent.doIt();
            }
        });
    }

    public String getValue() {
        return getText();
    }

    public String getIdentifiers() {
        return this.name;
    }

    public void setValue(String value) {
        String formatted = value;
        if (formater != null) {
            try {
                formatted = formater.format(Double.parseDouble(value));
            } catch (NumberFormatException nfe) {
            } catch (NullPointerException npe) {
                npe.printStackTrace();
            }
        }

        setText(formatted);
        setCaretPosition(0);
    }

}

interface AtomEditorEditors {
    String getIdentifiers();

    String getValue();

    void setValue(String value);

    void removeListeners();

    void restoreListeners();
}
