/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/10/07, 20:10
!  AUTHOR(S): KOGA, Junichiro
!  File : TableModelForAtomCoords.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.primitiveguis.tableforinputinterface;

import java.text.DecimalFormat;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Vector;

import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import org.apache.log4j.Logger;

import ciss.phase_viewer.atomcoord.Atom;
import ciss.phase_viewer.atomcoord.AtomCoords;
import ciss.phase_viewer.atomcoord.AtomList;
import ciss.phase_viewer.inputinterface.InputInterfaceEntryChangeEvent;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableColumns;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableSpecManager;

/**
 * AtomCoordsオブジェクト用のテーブルモデル. TableForInputInterfaceテーブルで利用 できるように
 * (InputInterfaceとは異なるのであまり美しくないけど).
 * 
 * @author
 */
public class TableModelForAtomCoords extends TableModelForInputInterface {
    private Logger logger = Logger.getLogger(TableModelForAtomCoords.class
            .getName());
    private InputInterfaceTableSpecManager manager;
    private AtomCoords coords;
    private AtomList list;
    private String[] identifiers;

    private boolean updateAtomCoords = true;

    private JTable parentTable;

    /**
     * Creates a new instance of TableModelForAtomCoords
     */
    public TableModelForAtomCoords(AtomCoords coords,
            String[] inputSpecifications,
            InputInterfaceTableSpecManager manager, int[] mapSpecCol,
            EventListener el, JTable parentTable) {
        super(inputSpecifications, manager, mapSpecCol, el);
        this.coords = coords;
        this.manager = manager;
        this.list = coords.getAtomList();
        this.parentTable = parentTable;
        this.initThis();
    }

    private void initThis() {
        createTableColumns();
        extractInputFile();
        setInputToModel();
    }

    protected void createTableColumns() {
        if (columns == null) {
            return;
        }

        for (int i = 0; i < mapSpecCol.length; i++) {
            addColumn(columns[mapSpecCol[i]].getName());
        }

        identifiers = new String[getColumnCount()];
        for (int i = 0; i < identifiers.length; i++) {
            identifiers[i] = getColumnName(i);
        }

        int nrw = manager.getTableSpec(inputSpecifications[0]).getNumRows();
        if (nrw > 0) {
            setRowCount(nrw); // setRowCountはテーブルモデルリスナーを誘発してしまうぞ. 気をつけろ.
        }
        // super.addTableModelListener( (TableModelListener) el );
    }

    protected void extractInputFile() {
    }

    protected void setInputToModel() {
        if (list == null) {
            return;
        }
        removeListeners();
        updateAtomCoords = false;
        for (int i = 0; i < list.size(); i++) {
            Atom atom = list.getAtomAt(i);
            for (int j = 0; j < getColumnCount(); j++) {
                String val = AtomCoords.getValueFrom(atom, identifiers[j]);
                setValueAt(val, i, j);
            }
            for (int j = 0; j < mapSpecCol.length; j++) {
                if (columns[mapSpecCol[j]].getDefaultValue().trim()
                        .equals("INCRE")
                        && columns[mapSpecCol[j]].ignoreOnSave()) {
                    setValueAt(new Integer(i + 1), i, j);
                }
            }
        }
        restoreListeners();
        setValueAt(getValueAt(0, 0), 0, 0);
        updateAtomCoords = true;

    }

    TableModelListener[] tlisteners;

    private void removeListeners() {
        tlisteners = ((DefaultTableModel) this).getTableModelListeners();
        if (tlisteners != null) {
            for (int i = 0; i < tlisteners.length; i++) {
                ((DefaultTableModel) this)
                        .removeTableModelListener(tlisteners[i]);
            }
        }
    }

    private void restoreListeners() {
        if (tlisteners != null) {
            for (int i = 0; i < tlisteners.length; i++) {
                ((DefaultTableModel) this).addTableModelListener(tlisteners[i]);
            }
        }
    }

    public void setUpdateAtomCoords(boolean updateAtomCoords) {
        this.updateAtomCoords = updateAtomCoords;
    }

    public void execute(EventObject e) {
        TableModelListener[] listeners = this.getTableModelListeners();

        for (int i = 0; i < listeners.length; i++) {
            this.removeTableModelListener(listeners[i]);
        }

        // obtaining indices associated with the change of this TableModel.
        TableModelEvent tmde = (TableModelEvent) e;
        int first = 0;
        int last = 0;
        if (tmde != null) {
            first = tmde.getFirstRow();
            last = tmde.getLastRow();
        } else {
            restoreListeners(listeners);
            return;
        }

        if (first < 0 || last < 0) {
            restoreListeners(listeners);
            return;
        }
        if (last >= getRowCount()) {
            restoreListeners(listeners);
            return;
        }

        for (int i = 0; i < mapSpecCol.length; i++) {
            if (columns[mapSpecCol[i]].getDefaultValue().trim().equals("INCRE")
                    && columns[mapSpecCol[i]].ignoreOnSave()) {
                setValueAt(new Integer(last + 1), last, i);
            }
        }

        if (updateAtomCoords) {
            updateAtomCoords(first, last);
        }
        restoreListeners(listeners);
    }

    private void updateAtomCoords(int first, int last) {
        int nullCount = 0;
        Vector id = new Vector();
        for (int i = 0; i < mapSpecCol.length; i++) {
            if (!columns[mapSpecCol[i]].ignoreOnSave()) {
                id.addElement(columns[mapSpecCol[i]].getName());
            }
        }
        if (id == null) {
            return;
        }
        String[] sid = new String[id.size()];
        id.copyInto(sid);

        for (int i = first; i < last + 1; i++) {
            Vector data = new Vector();
            for (int j = 0; j < mapSpecCol.length; j++) {
                InputInterfaceTableColumns col = columns[mapSpecCol[j]];
                if (col.ignoreOnSave()) {
                    continue;
                }

                Object obj = getValueAt(i, j);
                String string = "";

                if (obj instanceof String) {
                    string = (String) obj;
                    if (string == null) {
                        string = "";
                    }
                } else if (obj instanceof Boolean) {
                    boolean bool = ((Boolean) obj).booleanValue();
                    string = bool ? "1" : "0";
                }
                data.addElement(string);
            }

            if (data == null) {
                continue;
            }

            String[] sdata = new String[data.size()];
            data.copyInto(sdata);
            Atom atom = AtomCoords.createAtomFrom(sid, sdata);

            if (i >= list.size()) {
                list.addAtom(atom);
            } else if (i >= 0 && i < list.size()) {
                list.replaceAtomAt(i, atom);
            }
            logger.debug(list);
        }
    }

    public void removeRow(int row) {
        logger.debug("removing row.");
        if (row < list.size() && !(row < 0)) {
            list.removeAtomAt(row);
        }
        super.removeRow(row);
    }

    /**
     * フォーマットすべきデータはフォーマットするようにsetValueAtメソッドをオーバーライド.
     * 「フォーマットすべきかいなか」は設定ファイルに記述する.
     * 
     * @param data
     *            セットしたいデータ.
     * @param row
     *            セットする行.
     * @param col
     *            セットする列.
     */
    public void setValueAt(Object data, int row, int col) {
        // System.out.println(" data data, row, col: "+data+" "+row+" "+col);
        if (col >= mapSpecCol.length) {
            super.setValueAt(data, row, col);
            return;
        }
        if (!columns[mapSpecCol[col]].format()) {
            super.setValueAt(data, row, col);
        } else {
            DecimalFormat formatter = columns[mapSpecCol[col]].getFormatter();
            Object formatedData;
            if (data instanceof Double) {
                formatedData = (Object) formatter.format((Double) data);
            } else if (data instanceof String) {
                try {
                    Double d = Double.valueOf((String) data);
                    formatedData = (Object) formatter.format(d);
                } catch (NumberFormatException ne) {
                    formatedData = data;
                }
            } else {
                formatedData = data;
            }
            super.setValueAt(formatedData, row, col);
        }
    }

    /*
     * 以下はInputInterfaceを扱う時のみ必要. ここでは差し当たり不要と思われる.
     */
    public void inputInterfaceInitializing() {
    }

    public void inputInterfaceEntryChanged(InputInterfaceEntryChangeEvent e) {
    }

}
