/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : AtomCoords.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
 * 元素名や座標, セル情報など, 原子が持っている情報をまとめて管理するためのクラス群.
 * 原子そのものの属性などのほか, 原子の属性を変更するためのGUIのクラスや原子の状態の
 * 変更を通知するリスナーインターフェース, 編集の際のアンドゥリドゥ機能なども有する.
 * 座標データは他のプログラムと交換することも多いのでI/O機能は重要であり, 別パッケージに
 * している.
 */
package ciss.phase_viewer.atomcoord;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.table.TableModel;

import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Element;

import Jama.Matrix;
import ciss.phase_viewer.atomcoord.symmetry.BravaisLattice;
import ciss.phase_viewer.common.ConstParameters;
import ciss.phase_viewer.common.TaggedString;
import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.common.VectorOperations;
import ciss.phase_viewer.inputinterface.InputInterface;
import ciss.phase_viewer.inputinterface.InputInterfaceDefaults;
import ciss.phase_viewer.inputinterface.InputInterfaceEntryChangeEvent;
import ciss.phase_viewer.inputinterface.InputInterfaceEntryChangeListener;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTable;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableColumns;

/**
 * 原子配置を表すクラス.
 * 
 * @author
 */
public class AtomCoords implements Cloneable, InputInterfaceEntryChangeListener {
    private static Logger logger = Logger.getLogger(AtomCoords.class.getName());

    public static final int DEFAULT = 0;

    public static final int BOHR = 1;

    public static final int ANGSTROM = 2;

    public static final int NM = 3;

    public static final int BOHR2ANG = 4;

    public static final int ANG2BOHR = 5;

    public static final int TO_CART = 6;

    public static final int TO_INTERNAL = 7;

    public static final int TO_ANG = 8;

    public static final int TO_BOHR = 9;

    public static final int TO_NM = 10;

    private final double bohr_2_ang = 0.529177d;

    private final double ang_2_bohr = 1.d / bohr_2_ang;

    private AtomList atomList = new AtomList(this);

    private Cell[] cell = new Cell[2];

    private boolean iscart = true;

    private int unit;

    private DecimalFormat formater = ConstParameters.formater;

    private TableModel modelAt;

    private TableModel[] modelCe;

    // the identifier for element name, x-coordinate, y-coordinate and
    // z-coordinate
    private static String elems = "element";

    private static String rx = "rx";

    private static String ry = "ry";

    private static String rz = "rz";

    private VolumetricData chargeDensity;

    private Vector chargeDensities;

    /**
     * デフォルトコンストラクター, 特に何もしない.
     */
    public AtomCoords() {
    }

    public void nullify() {
        if (atomList != null)
            atomList.nullify();
        if (cell != null)
            cell = null;
        if (atomState != null)
            atomState = null;
    }

    private AtomStateList atomState;

    /**
     * このメソッドを呼ぶと, もう一度呼ばれるかfinalizeStateメソッドが呼ばれるまでの変更の履歴が保存される.
     */
    public void saveState() {
        atomState = new AtomStateList(getNumAt());
        atomList.setState(atomState);
    }

    /**
     * 原子の"状態"を取得; なんらかの変更を施した後に呼ぶとよい.
     * 
     * @return 原子の状態; saveState()メソッド以降の変更の簡単な履歴をゲット
     */
    public AtomStateList getAtomState() {
        return this.atomState;
    }

    /**
     * "原子の変更の履歴"の終了処理
     */
    public void finalizeState() {
        atomState = null;
    }

    /**
     * Atomオブジェクトの配列を格納する.
     * 
     * @param atom
     *            登録すべきAtomの配列
     */
    public AtomCoords(Atom[] atom) {
        for (int i = 0; i < atom.length; i++) {
            atomList.addAtom(atom[i]);
        }
    }

    /**
     * 原子の座標などのほか, セル情報もあらかじめ指定.
     * 
     * @param atom
     *            Atomオブジェクトの配列
     * @param cell
     *            Cellオブジェクトの配列; セルはベクトル表示(3x3行列) と a,b,c, alpha, beta,
     *            gamma表示(2x3行列) があり, cell[0]が前者, cell[1]が 後者であることを仮定している.
     */
    public AtomCoords(Atom[] atom, Cell cell[]) {
        for (int i = 0; i < atom.length; i++) {
            atomList.addAtom(atom[i]);
        }
        this.cell = cell;
    }

    /**
     * an atom always has an element name, x-coordinate, y-coordinate and
     * z-coordinate. the identifier used to distinguish these values may be
     * different from the default value. use this setter to change them from the
     * default values (which are "elements", "rx", "ry", and "rz").
     * 
     * @param elems
     *            the identifier for the element name.
     * @param rx
     *            the identifier for the x-coordinate.
     * @param ry
     *            the identifier for the y-coordinate.
     * @param rz
     *            the identifier for the z-coordinate.
     */
    public void setBasicIdentifiers(String elems, String rx, String ry,
            String rz) {
        this.elems = elems;
        this.rx = rx;
        this.ry = ry;
        this.rz = rz;
    }

    /**
     * Atomへの参照をもっているlinked listへの参照を返す.
     * 
     * @return AtomList
     */
    public AtomList getAtomList() {
        return this.atomList;
    }

    /**
     * transforms a 'cell' object to an inputinterface-entry object
     * 
     * @param from_cell
     *            どのcellスキームか; Cell.CELL_ABCかCell.Cell_VEC
     */
    public void atomCoords2InputInterface_cell(int from_cell) {
        if (from_cell == Cell.CELL_ABC) {
            convertCell(Cell.CELL_VEC);
        } else if (from_cell == Cell.CELL_VEC) {
            convertCell(Cell.CELL_ABC);
        }
    }

    /**
     * transforms the Atom object specified in args to an InputInterfaceEntry
     * object.
     * 
     * @param inputInterface
     *            the InputInterface object to which this operation is
     *            performed.
     * @param columns
     *            an array of InputInterfaceTableColumns object associated with
     *            the atom.
     * @param tag
     *            an identifier to specify the location of the
     *            InputInterfaceEntry object within inputInterface.
     * @param atom
     *            the atom object from which an InputInterfaceEntry object is
     *            created.
     * @param rowIndex
     *            the index to which atom is added.
     */
    private void atomCoords2InputInterface(InputInterface inputInterface,
            InputInterfaceTableColumns[] columns, String tag, Atom atom,
            int rowIndex) {
        String elem = atom.getElementName().trim();
        String[] pos = atom.getPos();
        TaggedString[] aux = atom.getAuxil();
        int numaux = 0;
        if (aux != null) {
            numaux = aux.length;
        }

        String[] str = new String[columns.length];
        String[] idents = new String[columns.length];
        for (int j = 0; j < columns.length; j++) {
            idents[j] = columns[j].getName();
            str[j] = "";
            if (columns[j].ignoreOnSave()) {
                continue;
            }
            if (columns[j].getName().equalsIgnoreCase(elems)) {
                str[j] = elem;
            } else if (columns[j].getName().equalsIgnoreCase(rx)) {
                str[j] = pos[0];
            } else if (columns[j].getName().equalsIgnoreCase(ry)) {
                str[j] = pos[1];
            } else if (columns[j].getName().equalsIgnoreCase(rz)) {
                str[j] = pos[2];
            } else {
                for (int k = 0; k < numaux; k++) {
                    if (aux[k].getTag().trim()
                            .equalsIgnoreCase(columns[j].getName().trim())) {
                        str[j] = aux[k].getValue();
                    }
                }
            }
            logger.debug("processing column: " + columns[j].getName()
                    + " whose value is: " + str[j]);
        }

        // #default識別子の処理
        InputInterfaceDefaults defs = inputInterface.getDefaults();
        if (defs != null) {
            HashMap map = defs.getDefaultValues();
            Object[] keys = map.keySet().toArray();
            for (int j = 0; j < columns.length; j++) {
                if (str[j].length() != 0)
                    continue;
                for (int i = 0; i < keys.length; i++) {
                    if (columns[j].getName().equalsIgnoreCase(
                            keys[i].toString()))
                        str[j] = map.get(keys[i]).toString();
                }
            }
        }

        InputInterfaceTable itable = inputInterface.getInputInterfaceTable(tag);
        itable.setWillChangeRow(rowIndex, InputInterfaceTable.REPLACE);
        itable.saveState();
        itable.replaceTableDataRow(idents, str, rowIndex);
    }

    /**
     * transforms this to the atom_list part of the inputInterface object.
     * 
     * @param inputInterface
     *            the InputInterface object to which this manipulation is
     *            performed.
     * @param tag
     *            the identifier for the atom_list part of the InputInterface
     *            object.
     */
    public void atomCoords2InputInterface(InputInterface inputInterface,
            String tag) {
        atomCoords2InputInterface_InitTable(inputInterface, tag, atomList, 0);
    }

    /**
     * transforms this to the atom_list part of the inputInterface object.
     * initializes tablular data prior to conversion.
     * 
     * @param inputInterface
     *            the InputInterface object to which this manipulation is
     *            performed.
     * @param tag
     *            the identifier for the atom_list part of the InputInterface
     *            object.
     * @param the
     *            AtomList object which holds the atomic data.
     * @param offset
     *            the "offset" for the atomlist.
     */
    public void atomCoords2InputInterface_InitTable(
            InputInterface inputInterface, String tag, AtomList list, int offset) {
        inputInterface.selectRoot();

        // i will assume that 'columns' will never be null (programmer's
        // responsibility!!).
        InputInterfaceTableColumns[] columns = inputInterface
                .getInputInterfaceTableSpecManager().getTableSpec(tag)
                .getTableColumns();
        InputInterfaceTable itable = inputInterface.getInputInterfaceTable(tag);
        logger.debug("init table: from 0" + " to "
                + itable.getTableData().size());
        // itable.removeTableDataRow(0,itable.getTableData().size());
        itable.removeAll();
        for (int i = 0; i < list.size(); i++) {
            Atom atom = list.getAtomAt(i);
            atomCoords2InputInterface(inputInterface, columns, tag, atom, i
                    + offset);
        }
        inputInterface.replaceEntry(itable, this);

        if (blat != null) {
            blat.saveToInputInterface(inputInterface);
        }

        inputInterface.selectRoot();
    }

    public void setBravaisLattice(BravaisLattice blat) {
        this.blat = blat;
    }

    /**
     * transforms this to the atom_list part of the inputInterface object. will
     * not initialize tablular data.
     * 
     * @param inputInterface
     *            the InputInterface object to which this manipulation is
     *            performed.
     * @param tag
     *            the identifier for the atom_list part of the InputInterface
     *            object.
     * @param the
     *            AtomList object which holds the atomic data.
     * @param offset
     *            the "offset" for the atomlist.
     */
    public void atomCoords2InputInterface(InputInterface inputInterface,
            String tag, AtomList list, int offset) {
        inputInterface.selectRoot();
        // i will assume that 'columns' will never be null (programmer's
        // responsibility!!).
        InputInterfaceTableColumns[] columns = inputInterface
                .getInputInterfaceTableSpecManager().getTableSpec(tag)
                .getTableColumns();

        InputInterfaceTable itable = inputInterface.getInputInterfaceTable(tag);
        for (int i = 0; i < list.size(); i++) {
            Atom atom = list.getAtomAt(i);
            atomCoords2InputInterface(inputInterface, columns, tag, atom, i
                    + offset);
        }
        inputInterface.replaceEntry(itable, this);

        inputInterface.selectRoot();
    }

    /**
     * transforms the cell to the "unit_cell" block of the inputInterface
     * object.
     * 
     * @param inputInterface
     *            the InputInterface object to which this transformation is
     *            performed.
     */
    public void cell2InputInterface(InputInterface inputInterface) {
        Cell cell = getCell(0);
        if (cell == null)
            cell = getCell(1);
        if (cell == null)
            return;

        String[][] cel = cell.getCell();
        if (cel.length == 3) {
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.a_vector").setValue(
                    cel[0][0] + " " + cel[0][1] + " " + cel[0][2]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.b_vector").setValue(
                    cel[1][0] + " " + cel[1][1] + " " + cel[1][2]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.c_vector").setValue(
                    cel[2][0] + " " + cel[2][1] + " " + cel[2][2]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.a").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.b").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.c").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.alpha").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.beta").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.gamma").setValue("");
        } else {
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.a_vector").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.b_vector").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.c_vector").setValue("");
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.a").setValue(cel[0][0]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.b").setValue(cel[0][1]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.c").setValue(cel[0][2]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.alpha").setValue(cel[1][0]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.beta").setValue(cel[1][1]);
            inputInterface.getInputInterfacePrimitiveEntry(
                    "structure.unit_cell.gamma").setValue(cel[1][2]);
        }

    }

    /**
     * transforms the atom_list part of the inputInterface object to a concrete
     * object of this class.
     * 
     * @param inputInterface
     *            the InputInterface object from which this manipulation is
     *            performed.
     * @param tag
     *            the identifier for the atom_list part of the InputInterface
     *            object.
     */
    public void inputInterface2AtomCoords(InputInterface inputInterface,
            String tag) {
        inputInterface.selectRoot();
        InputInterfaceTableColumns[] columns = inputInterface
                .getInputInterfaceTableSpecManager().getTableSpec(tag)
                .getTableColumns();
        InputInterfaceTable itable = inputInterface.getInputInterfaceTable(tag);
        String[] idents = itable.getColumnIdentifiers();
        inputInterface.selectRoot();
        Vector atoms = itable.getTableData();

        if (atoms.size() > atomList.size()) {
        }

        int nat = atoms.size();
        atomList.clear();

        for (int i = 0; i < nat; i++) {
            String[] atomData = (String[]) atoms.elementAt(i);

            String elementName = "";
            String[] pos = new String[3];
            Vector auvec = new Vector();

            for (int j = 0; j < idents.length; j++) {
                String dat = atomData[j];
                logger.debug("processing atom no. " + i + ", ident: "
                        + idents[j] + " value: " + dat);
                if (idents[j].trim().equalsIgnoreCase(elems)) {
                    dat = Utils.firstLetterCaps(dat);
                    elementName = dat;
                } else if (idents[j].trim().equalsIgnoreCase(rx)) {
                    pos[0] = dat;
                } else if (idents[j].trim().equalsIgnoreCase(ry)) {
                    pos[1] = dat;
                } else if (idents[j].trim().equalsIgnoreCase(rz)) {
                    pos[2] = dat;
                } else {
                    auvec.addElement(new TaggedString(idents[j].trim(), dat));
                }
            }

            atomList.addAtom(elementName, pos, auvec);
        }

        logger.debug("at inputInterface2AtomCoords: ");
        logger.debug(atomList);
    }

    /**
     * shifts the coordinates for the atoms back to their original values;
     * necessary if cell offset is not 0.
     */
    public void correctCellOffset() {
        try {
            Cell ce = getCell(0);
            double[] off = ce.getCellOffset();
            boolean shifted = false;
            for (int i = 0; i < off.length; i++) {
                if (off[i] != 0)
                    shifted = true;
            }
            if (!shifted)
                return;
            for (int i = 0; i < atomList.size(); i++) {
                Atom at = atomList.getAtomAt(i);
                double[] pos = at.getDouble();
                for (int j = 0; j < pos.length; j++) {
                    pos[j] += off[j];
                }
                at.setPos(pos);
            }
        } catch (Exception exc) {
            logger.error("invalid cell");
        }
    }

    /**
     * create an "Atom" object from an InputInterface object
     * 
     * @param inputInterface
     *            the InputInterface object from which the Atom object is
     *            created.
     * @param tag
     *            the identifier specifying the atom block.
     * @param index
     *            the index to specify the ID for the atom.
     */
    public Atom inputInterface2Atom(InputInterface inputInterface, String tag,
            int index) {
        InputInterfaceTableColumns[] columns = inputInterface
                .getInputInterfaceTableSpecManager().getTableSpec(tag)
                .getTableColumns();
        InputInterfaceTable itable = inputInterface.getInputInterfaceTable(tag);
        String[] idents = itable.getColumnIdentifiers();
        Vector atoms = itable.getTableData();

        if (atoms.size() <= index) {
            index = atoms.size() - 1;
        }
        if (index < 0) {
            index = 0;
        }

        String[] atomData = (String[]) atoms.elementAt(index);
        return createAtomFrom(idents, atomData);
    }

    /**
     * 識別子配列・データ配列よりAtomオブジェクトを作成する. 識別子やデータのチェックは 特に行わないので注意.
     * 
     * @param id
     *            識別子の配列.
     * @param data
     *            データ配列.
     * @return 結果得られるAtomオブジェクト
     */
    public static Atom createAtomFrom(String[] id, String[] data) {
        String elementName = "";
        String[] pos = new String[3];
        Vector auvec = new Vector();
        for (int i = 0; i < id.length; i++) {
            String dat = data[i];
            if (id[i].trim().equalsIgnoreCase(elems)) {
                // elementName = Utils.firstLetterCaps(dat);
                elementName = dat;
            } else if (id[i].trim().equalsIgnoreCase(rx)) {
                pos[0] = dat;
            } else if (id[i].trim().equalsIgnoreCase(ry)) {
                pos[1] = dat;
            } else if (id[i].trim().equalsIgnoreCase(rz)) {
                pos[2] = dat;
            } else {
                auvec.addElement(new TaggedString(id[i].trim(), dat));
            }
        }
        return new Atom(elementName, pos, auvec);
    }

    /**
     * Atomオブジェクトと識別子からそのAtomオブジェクトの対応する値を返す.
     * 
     * @param atom
     *            調べるAtomオブジェクト
     * @param id
     *            識別用のString.
     * @return Atomオブジェクトの, 対応する値.
     */
    public static String getValueFrom(Atom atom, String id) {
        String val = "";
        if (id.equalsIgnoreCase(elems)) {
            val = atom.getElementName();
        } else if (id.equalsIgnoreCase(rx)) {
            val = atom.getPos()[0];
        } else if (id.equalsIgnoreCase(ry)) {
            val = atom.getPos()[1];
        } else if (id.equalsIgnoreCase(rz)) {
            val = atom.getPos()[2];
        } else {
            TaggedString[] au = atom.getAuxil();
            if (au != null && au.length != 0) {
                for (int k = 0; k < au.length; k++) {
                    if (au[k].getTag().trim().equalsIgnoreCase(id)) {
                        val = au[k].getValue();
                        break;
                    }
                }
            }
        }
        return val;
    }

    public void inputInterfaceInitializing() {
        atomList.clear();
    }

    public void inputInterfaceInitialized() {
    }

    public void inputInterfaceEntryChanged(InputInterfaceEntryChangeEvent e) {
        // if ( e.getEntry().getEntryType() != InputInterfaceEntry.TABLE ||
        // e.getSource() == this ) {
        // return;
        // }
        // InputInterfaceTable itable = (InputInterfaceTable) e.getEntry();
        // String tag = e.getTag();
        // inputInterface2AtomCoords(e.getInputInterface(), tag);
    }

    /**
     * set the VolumetricData object associated with this atomic coordinates.
     * 
     * @param chargeDensity
     *            the VolumetricData object associated with the current class.
     */
    public void setChargeDensity(VolumetricData chargeDensity) {
        this.chargeDensity = chargeDensity;
        if (chargeDensities == null) {
            chargeDensities = new Vector();
        }
        chargeDensities.add(chargeDensity);
    }

    /**
     * set the VolumetricData object associated with this atomic coordinates.
     * see the VolumetricData class for details of the arguments.
     */
    public void setChargeDensity(float[] density, int[] ndiv, float[][] delta,
            float[] origin) {
        this.chargeDensity = new VolumetricData(density, ndiv, delta, origin);
        if (chargeDensities == null) {
            chargeDensities = new Vector();
        }
        chargeDensities.add(chargeDensity);
    }

    /**
     * add a VolumetricData object associated with this atomic coordinates.
     */
    public void addChargeDensity(float[] density, int[] ndiv, float[][] delta,
            float[] origin) {
        VolumetricData vdata = new VolumetricData(density, ndiv, delta, origin);
        addChargeDensity(vdata);
    }

    /**
     * add a VolumetricData object associated with this atomic coordinates.
     */
    public void addChargeDensity(VolumetricData vdata) {
        if (chargeDensities == null) {
            chargeDensities = new Vector();
        }
        chargeDensities.add(vdata);
    }

    /**
     * 新たにVolumetricDataを設定する. 昔のデータは廃棄される.
     * 
     * @param vdata
     *            新たにセットしたいボリュームデータの配列.
     */
    public void setChargeDensities(VolumetricData[] vdata) {
        if (vdata == null || vdata.length == 0)
            return;
        if (chargeDensities == null)
            chargeDensities = new Vector();
        chargeDensities.clear();
        for (int i = 0; i < vdata.length; i++)
            chargeDensities.add(vdata[i]);
    }

    private Vector primChargeDensities;

    /**
     * 'primitive'なセル用のVolumetricDataを設定する. Primitiveな場合を特にとっておく必要がある場合に利用する.
     * 
     * @param vdata
     *            設定したいVolumetricData
     */
    public void setChargeDensitiesPrimitiveCell(VolumetricData[] vdata) {
        if (vdata == null || vdata.length == 0)
            return;
        if (primChargeDensities == null)
            primChargeDensities = new Vector();
        primChargeDensities.clear();
        for (int i = 0; i < vdata.length; i++)
            primChargeDensities.add(vdata[i]);
    }

    /**
     * 'primitive'なセル用のVolumetricDataを取得.
     * 
     * @return 上記の通り
     */
    public VolumetricData[] getChargeDensitiesPrimitiveCell() {
        if (primChargeDensities == null)
            return null;
        VolumetricData[] vdata = new VolumetricData[primChargeDensities.size()];
        primChargeDensities.copyInto(vdata);
        return vdata;
    }

    /**
     * returns an array of volumetric data objects.
     * 
     * @return charge density data associated with this atomic coordinates
     */
    public VolumetricData[] getChargeDensities() {
        if (chargeDensities == null) {
            return null;
        }
        VolumetricData[] vdata = new VolumetricData[chargeDensities.size()];
        chargeDensities.copyInto(vdata);
        return vdata;
    }

    /**
     * get the VolumetricData object associated with this atomic coordinates.
     * 
     * @return chargeDensity the VolumetricData object associated with the
     *         current class.
     */
    public VolumetricData getChargeDensity() {
        return this.chargeDensity;
    }

    private double eps = 0.00001d;

    /**
     * 引数のatomが重なっていないかどうかチェック
     * 
     * @param atom
     *            テスト対象の原子
     */
    public boolean checkDupli(Atom atom) {
        boolean ret = false;
        for (int i = 0; i < atomList.size(); i++) {
            Atom check = atomList.getAtomAt(i);
            double[] dcheck = check.getDouble();
            double[] datom = atom.getDouble();
            double[] diff = { dcheck[0] - datom[0], dcheck[1] - datom[1],
                    dcheck[2] - datom[2] };
            if (VectorOperations.norm(diff) < eps) {
                ret = true;
            }
        }
        return ret;
    }

    /**
     * 重なっている原子を削除
     */
    public void removeDuplicateAtoms() {
        Vector dupli = new Vector();
        for (int i = atomList.size() - 1; i >= 1; i--) {
            for (int j = i - 1; j >= 0; j--) {
                if (j >= atomList.size()) {
                    continue;
                }
                Atom atomi = atomList.getAtomAt(i);
                Atom atomj = atomList.getAtomAt(j);
                double[] posi = atomi.getDouble();
                double[] posj = atomj.getDouble();
                double[] posdiff = { posi[0] - posj[0], posi[1] - posj[1],
                        posi[2] - posj[2] };
                double norm = VectorOperations.norm(posdiff);
                if (norm < eps) {
                    dupli.addElement(new Integer(i));
                }
            }
        }

        if (dupli.size() == 0) {
            return;
        }

        Integer[] idupli = new Integer[dupli.size()];
        dupli.copyInto(idupli);
        Arrays.sort(idupli);
        for (int i = idupli.length - 1; i >= 0; i--) {
            atomList.removeAtomAt(i);
        }
    }

    /**
     * 重なっている原子を, 周期境界条件も考慮して削除.
     */
    public void removeDupliCell() {
        if (!isCellValid(0)) {
            return;
        }
        Cell cell = getCell(0);
        double[][] dcell = cell.getDouble();
        Vector dupli = new Vector();
        for (int i = atomList.size() - 1; i >= 0; i--) {
            Atom atom = atomList.getAtomAt(i);
            double[] dpos = atom.getDouble();
            for (int a = -1; a <= 1; a++) {
                for (int b = -1; b <= 1; b++) {
                    for (int c = -1; c <= 1; c++) {
                        if (a == 0 && b == 0 && c == 0) {
                            continue;
                        }
                        double[] tmppos = new double[3];
                        for (int j = 0; j < 3; j++) {
                            tmppos[j] = dpos[j] + dcell[0][j] * a + dcell[1][j]
                                    * b + dcell[2][j] * c;
                        }
                        if (checkDupli(new Atom(atom.getElementName(), tmppos,
                                atom.getAuxil()))) {
                            logger.info("removed duplicate atom, No. " + i);
                            atomList.removeAtom(atom);
                        }
                    }
                }
            }
        }
    }

    /**
     * '仮想的な原子'を削除する
     */
    public void removeBoundaryAtoms() {
        for (int i = atomList.size() - 1; i >= 0; i--) {
            Atom at = atomList.getAtomAt(i);
            if (at.isBoundaryAtom())
                atomList.removeAtomAt(i);
        }
    }

    /**
     * 副格子の原子を削除する
     */
    public void removeSubLatticeAtoms() {
        for (int i = atomList.size() - 1; i >= 0; i--) {
            Atom at = atomList.getAtomAt(i);
            if (at.isSubLatticeAtom())
                atomList.removeAtomAt(i);
        }
    }

    /**
     * 描画上の都合で存在している原子を削除する.
     */
    public void removeVirtualAtoms() {
        for (int i = atomList.size() - 1; i >= 0; i--) {
            Atom a = atomList.getAtomAt(i);
            if (a.isVirtualAtom())
                atomList.removeAtomAt(i);
        }
    }

    /**
     * 「保存する原子」以外を削除.
     */
    public void removeIgnoreOnSaveAtoms() {
        for (int i = atomList.size() - 1; i >= 0; i--) {
            Atom a = atomList.getAtomAt(i);
            if (a.isSubLatticeAtom() && a.saveSubLatticeAtom())
                continue;
            else if (a.isVirtualAtom())
                atomList.removeAtomAt(i);
            else if (a.isInvSymAtom())
                atomList.removeAtomAt(i);
        }
    }

    /**
     * このオブジェクトのコピーを作成; ただ, 未完成
     * 
     * @return このオブジェクトのコピー
     */
    public AtomCoords getCopy() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            logger.error("clone failed");
            return null;
        }
        return (AtomCoords) o;
    }

    /**
     * セルの配列を取得
     * 
     * @return セル; 0番目が{{ax,ay,az},{bx,by,bz},{cx,cy,cz}}という3x3の配列,
     *         1番目が{{a,b,c},{alpha,beta,gamma}}という2x3配列.
     */
    public Cell[] getCell() {
        return this.cell;
    }

    /**
     * セルをセット
     * 
     * @param cell
     *            セル, getCellと同様,
     *            0番目が{{ax,ay,az},{bx,by,bz},{cx,cy,cz}}という3x3の配列,
     *            1番目が{{a,b,c},{alpha,beta,gamma}}という2x3配列を用意すること.
     */
    public void setCell(Cell[] cell) {
        this.cell = cell;

    }

    /**
     * セルを, 一種類だけセット. またその際にアンドゥリドゥ用スタックを更新するか どうかを選べる
     * 
     * @param cell_
     *            セットしたいセル
     * @param updateStack
     *            スタックを更新する場合true.
     */
    public void setCell(Cell cell_, boolean updateStack) {
        int cellscheme = cell_.getCellScheme();
        if (updateStack) {
            atomList.getUndoRedoObject().addEntry(getCell(cellscheme));
        }

        if (cellscheme == 0 || cellscheme == 1) {
            this.cell[cellscheme] = cell_;
        } else {
            logger.warn("invalid cell scheme");
        }

        boolean b0 = isCellValid(0);
        boolean b1 = isCellValid(1);
        if (b1 && !b0) {
            if (!convertCell(Cell.CELL_VEC)) {
                logger.warn("failed conversion of cell");
            }
        } else if (!b1 && b0) {
            if (!convertCell(Cell.CELL_ABC)) {
                logger.warn("failed conversion of cell");
            }
        }
    }

    /**
     * セルを, 一種類だけセット. その際にアンドゥリドゥ用スタックは更新される.
     * 
     * @param cell_
     *            セットしたいセル
     */
    public void setCell(Cell cell_) {
        setCell(cell_, true);
    }

    /**
     * セルを取得, 引数によってどっちが欲しいかを選択できる.
     * 0は{{ax,ay,az},{bx,by,bz},{cx,cy,cz}}という3x3の配列,
     * 1は{{a,b,c},{alpha,beta,gamma}}という2x3配列で返す.
     * 
     * @param i
     *            0か1を指定
     * @return 欲しいセル
     */
    public Cell getCell(int i) {
        Cell cell = null;
        if (i == 0 || i == 1) {
            cell = this.cell[i];
        }
        return cell;
    }

    /**
     * セルを, double配列として取得. それ以外はgetCellと同様だが, doubleにできない場合nullを返す.
     * 
     * @param i
     *            0か1
     * @return double配列で表したセル
     */
    public double[][] getCellDouble(int i) {
        Cell cell = this.cell[i];
        String[][] cellString = cell.getCell();
        int irow = 2;
        if (i == 0) {
            irow = 3;
        }
        double[][] cellDouble = new double[irow][3];
        try {
            for (int ir = 0; ir < irow; ir++) {
                for (int j = 0; j < 3; j++) {
                    cellDouble[ir][j] = Double.valueOf(cellString[ir][j])
                            .doubleValue();
                }
            }
        } catch (NumberFormatException nfe) {
            cellDouble = null;
        } catch (NullPointerException npe) {
            cellDouble = null;
        }

        return cellDouble;
    }

    /**
     * 原子の数を取得
     * 
     * @return 原子の数
     */
    public int getNumAt() {
        return atomList.size();
    }

    /**
     * distinctな元素の配列を取得
     * 
     * @return distinctな元素のString配列
     */
    public String[] getDistinctElements() {
        return distinctElements();
    }

    /**
     * 普通のデカルト座標で原子配置が指定されているかどうかをセット
     * 
     * @param iscart
     *            デカルト座標の場合true
     */
    public void isCart(boolean iscart) {
        this.iscart = iscart;
    }

    /**
     * 単位をセット.
     * 
     * @param unit
     *            単位; BOHR,ANGSTROM,NMが選べる
     */
    public void setUnit(int unit) {
        this.unit = unit;
        logger.debug("set unit: " + unit);
    }

    /**
     * 単位を取得
     * 
     * @return 単位情報; BOHR, ANGSTROM, NMのいずれかを返す
     */
    public int getUnit() {
        return this.unit;
    }

    /**
     * このオブジェクトが表す座標がデカルト座標で表されているかどうかを返す.
     * 
     * @return デカルト座標ならtrue
     */
    public boolean isCart() {
        return iscart;
    }

    /**
     * !isCart()と同じ
     * 
     * @return 内部座標の場合true
     */
    public boolean isInternal() {
        return !iscart;
    }

    /**
     * セルの指定が「妥当」か否か判定する
     * 
     * @param cellscheme
     *            セルの指定方法; 0か1を渡す
     * @return 妥当でない場合false
     */
    public boolean isCellValid(int cellscheme) {
        if (cellscheme < 0 || cellscheme > 1) {
            return false;
        }
        return checkCell(cellscheme);
    }

    private double[][] toCart(double[][] pos) {
        Cell validCell = null;
        int NumAt = pos.length;
        double Dpos[][] = new double[NumAt][3];
        if (isCellValid(0)) {
            validCell = cell[0];
        } else if (isCellValid(1)) {
            if (!convertCell(Cell.CELL_VEC)) {
                logger.warn("failed conversion of cell");
                return null;
            }
            validCell = cell[0];
        } else {
            logger.warn("you must specify a valid cell for 'internal' coord scheme");
            return null;
        }
        String[][] cellString = validCell.getCell();
        try {
            for (int i = 0; i < NumAt; i++) {
                double[] vec = new double[3];
                for (int j = 0; j < 3; j++) {
                    for (int k = 0; k < 3; k++) {
                        vec[k] += pos[i][j]
                                * Double.parseDouble(cellString[j][k]);
                    }
                }
                for (int j = 0; j < 3; j++) {
                    // Dpos[i][j] = getAngstrom(vec[j]);
                    Dpos[i][j] = vec[j];
                }
            }
        } catch (NumberFormatException ne) {
            logger.warn("couldn't convet to 'cart'");
            return null;
        }
        return Dpos;
    }

    private double[][] toCart() {
        Cell validCell = null;
        int NumAt = getNumAt();
        double Dpos[][] = new double[NumAt][3];
        if (isCellValid(0)) {
            validCell = cell[0];
        } else if (isCellValid(1)) {
            if (!convertCell(Cell.CELL_VEC)) {
                logger.warn("failed conversion of cell");
                return null;
            }
            validCell = cell[0];
        } else {
            logger.warn("you must specify a valid cell");
            return null;
        }
        String[][] cellString = validCell.getCell();
        try {
            for (int i = 0; i < atomList.size(); i++) {
                Atom atom = atomList.getAtomAt(i);
                String[] p = atom.getPos();
                double[] vec = new double[3];
                for (int j = 0; j < 3; j++) {
                    for (int k = 0; k < 3; k++) {
                        vec[k] += Double.parseDouble(p[j])
                                * Double.parseDouble(cellString[j][k]);
                    }
                }
                for (int j = 0; j < 3; j++) {
                    // Dpos[i][j] = getAngstrom(vec[j]);
                    Dpos[i][j] = vec[j];
                }
            }
        } catch (NumberFormatException ne) {
            logger.warn("couldn't convet to 'cart'");
            return null;
        }
        return Dpos;
    }

    private double[][] toInternal(double[][] pos) {
        double[][] ret = null;

        if (!isCellValid(0)) {
            if (!abcToVec()) {
                logger.warn("failed to get cell info ...");
                return ret;
            }
        }

        double[][] ce = cell[0].getDouble();
        double[][] cet = new double[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                cet[i][j] = ce[j][i];
            }
        }

        double tmp = 0.d;
        int numat = pos.length;
        ret = new double[numat][3];

        for (int i = 0; i < numat; i++) {
            double[] Dpos = pos[i];
            double[][] bvec = new double[3][1];
            for (int i1 = 0; i1 < 3; i1++) {
                bvec[i1][0] = Dpos[i1];
            }

            // --- Jama ---
            Matrix A = new Matrix(cet);
            Matrix b = new Matrix(bvec);
            Matrix x = A.solve(b);
            // --- Jama ---

            for (int i1 = 0; i1 < 3; i1++) {
                ret[i][i1] = x.get(i1, 0);
                logger.debug("retret,i,i1: " + i + " " + i1 + " " + ret[i][i1]);
            }
        }

        return ret;
    }

    private double[][] toInternal() {
        double[][] ret = null;
        if (!isCellValid(0)) {
            if (!abcToVec()) {
                logger.warn("failed to get cell info ...");
                return ret;
            }
        }

        double[][] ce = cell[0].getDouble();
        double[][] cet = new double[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                cet[i][j] = ce[j][i];
            }
        }
        int numat = atomList.size();
        double tmp = 0.d;
        ret = new double[numat][3];

        for (int i = 0; i < numat; i++) {
            Atom atom = atomList.getAtomAt(i);
            double[] Dpos = atom.getDouble();
            double[][] bvec = new double[3][1];
            for (int i1 = 0; i1 < 3; i1++) {
                bvec[i1][0] = Dpos[i1];
            }

            // --- Jama ---
            Matrix A = new Matrix(cet);
            Matrix b = new Matrix(bvec);
            Matrix x = A.solve(b);
            // --- Jama ---

            for (int i1 = 0; i1 < 3; i1++) {
                ret[i][i1] = x.get(i1, 0);
            }
        }

        return ret;
    }

    /**
     * always returns in angstrom units
     */
    private double[][] toDouble() {
        int NumAt = getNumAt();
        double[][] Dpos = new double[NumAt][3];
        String[][] pos = new String[NumAt][3];
        for (int i = 0; i < NumAt; i++) {
            Atom atom = atomList.getAtomAt(i);
            String[] p = atom.getPos();
            for (int j = 0; j < 3; j++) {
                try {
                    // Dpos[i][j] = getAngstrom(Double.parseDouble(p[j]));
                    Dpos[i][j] = Double.parseDouble(p[j]);
                } catch (NumberFormatException nfe) {
                    logger.warn("non-numeric coordinates found for atom " + i
                            + ", " + j + "-th coordinate");
                    return null;
                }
            }
        }

        return Dpos;
    }

    /**
     * double配列で全原子の座標を返す. 第一要素が原子のインデックス, 第二要素がx,y,z
     * 
     * @return double配列の座標
     */
    public double[][] getPosDouble() {
        double[][] ret = null;
        if (!iscart) {
            ret = toCart();
        } else {
            ret = toDouble();
        }
        return ret;
    }

    /**
     * double配列で全原子に働く力を返す. 第一要素が原子のインデックス, 第二要素がx,y,z
     * 
     * @return double配列で表記した, 全原子にかかる力
     */
    public double[][] getForceDouble() {
        double[][] ret = new double[getNumAt()][3];
        for (int i = 0; i < getNumAt(); i++) {
            String[] tmp = atomList.getAtomAt(i).getForce();
            try {
                for (int j = 0; j < 3; j++) {
                    ret[i][j] = Double.parseDouble(tmp[j]);
                }
            } catch (Exception ex) {
                return ret;
            }
        }
        return ret;
    }

    /**
     * 原子の座標をdouble配列でセット. 第一要素が原子のインデックス, 第二要素がx,y,z
     * 
     * @param pos
     *            セットしたい原子の座標の配列
     */
    public void setPosDouble(double[][] pos) {
        setPosDouble(pos, iscart);
    }

    /**
     * convert input to the unit currently specified in this class.
     * 
     * @param value
     *            the original value
     * @param from_length
     *            the original unit
     */
    private double convertLength(double value, int from_length) {
        double ret = value;

        double scale = 1.d;
        if (from_length == TO_ANG && unit != ANGSTROM) {
            if (unit == BOHR || unit == DEFAULT) {
                scale = ang_2_bohr;
            } else if (unit == NM) {
                scale = 0.1d;
            }
        } else if (from_length == TO_BOHR && (unit != BOHR || unit != DEFAULT)) {
            if (unit == ANGSTROM) {
                scale = bohr_2_ang;
            } else if (unit == NM) {
                scale = bohr_2_ang * 0.1d;
            }
        } else if (from_length == TO_NM && unit != NM) {
            if (unit == ANGSTROM) {
                scale = 10.d;
            } else if (unit == BOHR || unit == DEFAULT) {
                scale = bohr_2_ang * 10.d;
            }
        } else {
            return ret;
        }
        ret *= scale;

        return ret;
    }

    /**
     * 指定の原子を差し替える. AtomListへの参照を取得してからreplaceAtomAtを発行するのと同じ.
     * 
     * @param index
     *            このインデックスで指定される原子を差し替える
     * @param atom
     *            新しい原子
     */
    public void replaceAtomAt(int index, Atom atom) {
        if (index >= atomList.size() || index < 0) {
            atomList.addAtom(atom);
            return;
        }
        atomList.replaceAtomAt(index, atom);
    }

    /**
     * sets position data of atoms.
     * 
     * @param pos
     *            the position data to be set (we assume that they are in
     *            angstrom units.)
     * @param cart
     *            true if 'pos' is in cartesian representation.
     */
    public void setPosDouble(double[][] pos, boolean cart) {
        if (pos == null) {
            logger.error("'pos' is null... ");
            return;
        }
        double[][] tmp = getTransformedPos(pos, cart);
        // double [][] tmp = pos;

        int n = tmp.length;
        int numAt = atomList.size();
        if (numAt < n) {
            logger.warn("numAt < pos.length; using numAt.");
            n = numAt;
        }
        for (int i = 0; i < n; i++) {
            Atom at = atomList.getAtomAt(i);
            at.setPos(tmp[i]);
            atomList.replaceAtomAt(i, at);
        }
    }

    private double[][] getTransformedPos(double[][] pos, boolean cart) {
        double[][] tpos = new double[pos.length][3];
        double[][] tmp = new double[pos.length][3];

        tpos = pos;
        if (unit != ANGSTROM && cart) {
            for (int i = 0; i < pos.length; i++) {
                for (int j = 0; j < 3; j++) {
                    tpos[i][j] = convertLength(pos[i][j], TO_ANG);
                }
            }
        }

        if (cart) {
            if (iscart) {
                // do nothing, just copy tpos->tmp
                tmp = tpos;
            } else {
                // cart->internal
                tmp = toInternal(tpos);
            }
        } else {
            if (!iscart) {
                // do nothing, just copy tpos->tmp
                tmp = tpos;
            } else {
                // internal->cart
                tmp = toCart(tpos);
            }
        }
        return tmp;
    }

    /**
     * 元素名の配列を返す. 配列のインデックスは, 登録されているAtomListと同じ.
     * 
     * @return 元素名配列
     */
    public String[] getElements() {
        int NumAt = getNumAt();
        String[] elements = new String[NumAt];
        for (int i = 0; i < NumAt; i++) {
            Atom atom = atomList.getAtomAt(i);
            elements[i] = atom.getElementName();
        }
        return elements;
    }

    /**
     * 全原子のauxiliaryな情報(たとえば, phaseにおける各原子のmobility)を格納した配列を返す.
     * TaggedString#getTag()でその情報の名前, TaggedString#getValue()でその値をゲットすることができる
     * 
     * @return 全原子のauxiliaryな情報
     */
    public TaggedString[][] getAuxils() {
        int NumAt = getNumAt();
        Vector auxvec = new Vector();
        int numauxs = 0;
        for (int i = 0; i < NumAt; i++) {
            Atom atom = atomList.getAtomAt(i);
            TaggedString[] ts = atom.getAuxil();
            if (ts != null && ts.length != 0) {
                if (numauxs < ts.length) {
                    numauxs = ts.length;
                }
                auxvec.addElement(ts);
            }
        }

        if (numauxs == 0) {
            logger.debug("auxiliary info not available");
            return null;
        }
        TaggedString[][] auxils = new TaggedString[NumAt][numauxs];
        for (int i = 0; i < auxvec.size(); i++) {
            TaggedString[] ts = (TaggedString[]) auxvec.get(i);
            for (int j = 0; j < ts.length; j++) {
                auxils[i][j] = ts[j];
            }
        }

        return auxils;
    }

    /**
     * 長さの単位, 内部座標とデカルト座標, を変換する
     * 
     * @param to_coords
     *            変換先の座標の取り方; 負の値の場合何もしない.
     * @param to_length
     *            変換先の長さの単位; 負の値の場合なにもしない.
     */
    public void convert(int to_coordsys, int to_length) {
        convertCoordSys(to_coordsys);
        convertLength(to_length);
        logger.debug(atomList);

        if (to_length == TO_BOHR)
            unit = BOHR;
        if (to_length == TO_ANG)
            unit = ANGSTROM;
        if (to_coordsys == TO_INTERNAL)
            this.iscart = false;
        if (to_coordsys == TO_CART)
            this.iscart = true;
    }

    private void convertCoordSys(int to_coordsys) {
        double[][] tmp = null;

        if (to_coordsys == TO_CART && !isCart()) {
            // internal -> cart
            tmp = this.toCart();
            iscart = true;
        } else if (to_coordsys == TO_INTERNAL && isCart()) {
            // cart->internal
            tmp = this.toInternal();
            iscart = false;
        } else {
            return;
        }
        if (tmp == null) {
            return;
        }

        int numat = tmp.length;
        if (numat == atomList.size()) {
            for (int i = 0; i < numat; i++) {
                atomList.getAtomAt(i).setPos(tmp[i]);
            }
        } else {
            logger.warn("number of atoms & atom list inconsistent!");
        }
        this.vecToABC();
    }

    private void convertLength(int to_length) {
        int numat = atomList.size();
        if (numat == 0) {
            return;
        }

        double scale = 1.d;
        if (to_length == TO_ANG && unit != ANGSTROM) {
            if (unit == BOHR || unit == DEFAULT) {
                scale = bohr_2_ang;
            } else if (unit == NM) {
                scale = 10.d;
            }
            unit = ANGSTROM;
        } else if (to_length == TO_BOHR && (unit != BOHR || unit != DEFAULT)) {
            if (unit == ANGSTROM) {
                scale = ang_2_bohr;
            } else if (unit == NM) {
                scale = ang_2_bohr * 10.d;
            }
            unit = BOHR;
        } else if (to_length == TO_NM && unit != NM) {
            if (unit == ANGSTROM) {
                scale = 0.1d;
            } else if (unit == BOHR || unit == DEFAULT) {
                scale = bohr_2_ang * 0.1d;
            }
            unit = NM;
        } else {
            return;
        }
        if (cell[0] != null) {
            double[][] dcell = cell[0].getDouble();
            if (dcell != null) {
                double[][] scell = new double[3][3];
                for (int i = 0; i < 3; i++) {
                    for (int j = 0; j < 3; j++) {
                        scell[i][j] = dcell[i][j] * scale;
                    }
                }
                cell[0] = new Cell(scell[0], scell[1], scell[2]);
            }
        }

        if (cell[1] != null) {
            double[][] dcell2 = cell[1].getDouble();
            if (dcell2 != null) {
                double[][] scell = new double[2][3];
                for (int i = 0; i < 3; i++) {
                    scell[0][i] = dcell2[0][i] * scale;
                    scell[1][i] = dcell2[1][i];
                }
                cell[1] = new Cell(scell[0][0], scell[0][1], scell[0][2],
                        scell[1][0], scell[1][1], scell[1][2]);
            }
        }
        if (isCart()) {
            for (int i = 0; i < atomList.size(); i++) {
                Atom atom = atomList.getAtomAt(i);
                String[] pos = atom.getPos();
                double[] tmp = new double[3];
                try {
                    for (int j = 0; j < 3; j++) {
                        tmp[j] = Double.parseDouble(pos[j]) * scale;
                    }
                } catch (NumberFormatException nfe) {
                    logger.warn("failed conversion of units for atom no. " + i);
                    return;
                }
                atom.setPos(tmp);
            }
        }
    }

    /**
     * updateTableメソッドを呼んだ際に反映されるテーブルモデルを登録する.
     * 
     * @param modelAt
     *            原子のテーブルモデル.
     * @param modelCe
     *            セルのテーブルモデル. modelCell[0]がvector表示,
     *            modelCell[1]がa,b,c,alpha,beta,gamma表示に対応
     */
    public void setModels(TableModel modelAt, TableModel[] modelCe) {
        this.modelAt = modelAt;
        this.modelCe = modelCe;
    }

    /**
     * テーブルモデルからセルを作成, このクラスに登録.
     * 
     * @param modelCell
     *            セル情報を持つテーブルモデル.
     */
    public void createCellFromTableModel(TableModel modelCell) {
        if (modelCell == null) {
            logger.warn("invalid atom table.");
            return;
        }

        // we assume that modelCell[0] has dimension 3x4 and modelCell[1] has
        // dimension 1x4,
        // and that the first column is just an identifier.
        int cellsc = 0;
        TableModel model1 = modelCell;
        if (model1.getRowCount() == 1)
            cellsc = 1;

        if (cellsc == 0) {
            String[] avec = { (String) model1.getValueAt(0, 1),
                    (String) model1.getValueAt(0, 2),
                    (String) model1.getValueAt(0, 3) };
            String[] bvec = { (String) model1.getValueAt(1, 1),
                    (String) model1.getValueAt(1, 2),
                    (String) model1.getValueAt(1, 3) };
            String[] cvec = { (String) model1.getValueAt(2, 1),
                    (String) model1.getValueAt(2, 2),
                    (String) model1.getValueAt(2, 3) };
            cell[0] = new Cell(avec, bvec, cvec);
        } else {
            cell[1] = new Cell((String) model1.getValueAt(0, 1),
                    (String) model1.getValueAt(0, 2),
                    (String) model1.getValueAt(0, 3),
                    (String) model1.getValueAt(0, 4),
                    (String) model1.getValueAt(0, 5),
                    (String) model1.getValueAt(0, 6));
        }

        setCell(cell);

        if (isCellValid(1) && !isCellValid(0)) {
            if (!convertCell(Cell.CELL_VEC)) {
                logger.warn("failed conversion of cell");
            }
        } else if (!isCellValid(1) && isCellValid(0)) {
            if (!convertCell(Cell.CELL_ABC)) {
                logger.warn("failed conversion of cell");
            }
        }

    }

    /**
     * テーブルモデルからセルを作成, このクラスに登録.
     * 
     * @param modelCell
     *            セル情報を持つテーブルモデル. modelCell[0]がvector表示,
     *            modelCell[1]がa,b,c,alpha,beta,gamma表示に対応
     */
    public void createCellFromTableModel(TableModel[] modelCell) {
        if (modelCell == null) {
            logger.warn("invalid atom table.");
            return;
        }
        if (modelCell.length != 2 || modelCell[0] == null
                || modelCell[1] == null) {
            logger.warn("invalid cell table.");
            return;
        }

        // we assume that modelCell[0] has dimension 3x4 and modelCell[1] has
        // dimension 1x4,
        // and that the first column is just an identifier.
        TableModel model1 = modelCell[0];
        TableModel model2 = modelCell[1];

        String[] avec = { (String) model1.getValueAt(0, 1),
                (String) model1.getValueAt(0, 2),
                (String) model1.getValueAt(0, 3) };
        String[] bvec = { (String) model1.getValueAt(1, 1),
                (String) model1.getValueAt(1, 2),
                (String) model1.getValueAt(1, 3) };
        String[] cvec = { (String) model1.getValueAt(2, 1),
                (String) model1.getValueAt(2, 2),
                (String) model1.getValueAt(2, 3) };
        Cell[] cell = new Cell[2];
        cell[0] = new Cell(avec, bvec, cvec);
        cell[1] = new Cell((String) model2.getValueAt(0, 1),
                (String) model2.getValueAt(0, 2), (String) model2.getValueAt(0,
                        3), (String) model2.getValueAt(0, 4),
                (String) model2.getValueAt(0, 5), (String) model2.getValueAt(0,
                        6));

        setCell(cell);

        if (isCellValid(1) && !isCellValid(0)) {
            if (!convertCell(Cell.CELL_VEC)) {
                logger.warn("failed conversion of cell");
            }
        } else if (!isCellValid(1) && isCellValid(0)) {
            if (!convertCell(Cell.CELL_ABC)) {
                logger.warn("failed conversion of cell");
            }
        }

    }

    /**
     * setModelsで登録されたテーブルモデルを, このオブジェクトの内容で書き換える.
     */
    public void updateTable() {
        updateTable(modelAt, modelCe);
    }

    /**
     * 単位情報の文字列representationを返す.
     * 
     * @param u
     *            単位情報
     * @return 文字列で表された単位情報
     */
    public String getUnitString(int u) {
        if (u == BOHR) {
            return "bohr";
        } else if (u == ANGSTROM) {
            return "angstrom";
        } else if (u == NM) {
            return "nm";
        } else {
            return "bohr";
        }
    }

    /**
     * 単位情報はint値で保存されているけど, それをStringで指定する場合に利用.
     * 
     * @param u
     *            単位のString表現(bohr, angstrom, or nm)
     * @return 単位を表すint値
     */
    public int getUnitInt(String u) {
        if (u == null || u.length() == 0 || u.equalsIgnoreCase("bohr")) {
            return BOHR;
        } else if (u.equalsIgnoreCase("angstrom")) {
            return ANGSTROM;
        } else if (u.equalsIgnoreCase("nm")) {
            return NM;
        } else {
            return BOHR;
        }
    }

    /**
     * このオブジェクトを, XMLのエレメントに変換
     * 
     * @param numFrame
     *            このAtomCoordsが表すフレーム番号
     * @return AtomCoordsオブジェクトのXMLElement.
     */
    public Element getXMLElement(int numFrame) {
        Element element = new Element("frame");
        element.setAttribute(new Attribute("framenum", String.valueOf(numFrame)));
        element.setAttribute(new Attribute("iscart", String.valueOf(iscart)));
        String strunit = getUnitString(unit);
        element.setAttribute(new Attribute("lengthunit", strunit));
        Element elemList = atomList.getXMLElement();
        element.addContent(elemList);
        if (cell[0] != null) {
            element.addContent(cell[0].getXMLElement());
        }
        if (cell[1] != null) {
            element.addContent(cell[1].getXMLElement());
        }
        return element;
    }

    private void updateTableCell() {
        if (modelCe == null) {
            return;
        }
        if (modelCe.length != 2 || modelCe[0] == null || modelCe[1] == null) {
            return;
        }

        String[][] cell1 = getCell()[0].getCell();
        String[][] cell2 = getCell()[1].getCell();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                String form = "";
                try {
                    form = formater.format(Double.parseDouble(cell1[i][j]));
                } catch (Exception ex) {
                    form = cell1[i][j];
                }
                modelCe[0].setValueAt(form, i, j + 1);
            }
        }
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 3; j++) {
                String form = "";
                try {
                    form = formater.format(Double.parseDouble(cell2[i][j]));
                } catch (Exception ex) {
                    form = cell2[i][j];
                }
                modelCe[1].setValueAt(form, 0, 1 + j + i * 3);
            }
        }
    }

    /**
     * 引数で指定しているテーブルモデルを, このオブジェクトの内容で書き換える.
     * 
     * @param model
     *            原子配置の情報を格納すべきテーブルモデル
     * @param modelCell
     *            セル情報を格納すべきテーブルモデル. modelCell[0]がvector表示,
     *            modelCell[1]がa,b,c,alpha,beta,gamma表示に対応
     */
    public void updateTable(TableModel model, TableModel[] modelCell) {
        if (model != null) {
            int row = model.getRowCount();
            int column = model.getColumnCount();
            Utils.clearTableModel(model);

            String[] columnName = new String[column];
            for (int i = 0; i < column; i++) {
                columnName[i] = model.getColumnName(i);
            }

            if (column < 4) {
                logger.warn("not a valid table model.");
                return;
            }

            if (row < atomList.size()) {
                logger.warn("not enough table size...");
                return;
            }

            logger.debug("updating table... atomList: " + atomList);

            // we assume that the zero-th column consists from consecutive
            // integers.
            for (int i = 0; i < atomList.size(); i++) {
                model.setValueAt(new Integer(i + 1), i, 0);
            }

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

                logger.debug("setting info for atom " + i);
                Atom atom = atomList.getAtomAt(i);
                String name = atom.getElementName();
                String[] pos = atom.getPos();
                logger.debug("setting element name ..." + name);
                model.setValueAt(name, i, 1);
                logger.debug("pos for atom " + i + " is " + pos[0] + " "
                        + pos[1] + " " + pos[2]);
                for (int j = 0; j < 3; j++) {
                    model.setValueAt(pos[j], i, j + 2);
                }
                TaggedString[] auxil = atom.getAuxil();
                if (auxil != null) {
                    for (int j = 0; j < auxil.length; j++) {
                        String ident = auxil[j].getTag();
                        int ia = -1;
                        for (int k = 0; k < column; k++) {
                            if (ident.trim().equalsIgnoreCase(
                                    columnName[k].trim())) {
                                ia = k;
                                break;
                            }
                        }
                        if (ia != -1) {
                            model.setValueAt(auxil[j].getValue(), i, ia);
                        }
                    }
                }
            }
        }

        if (modelCell != null) {
            for (int i = 0; i < 2; i++) {
                if (cell[i] == null) {
                    continue;
                }
                String[][] cellData = cell[i].getCell();
                int roww = 0;
                if (i == Cell.CELL_VEC) {
                    roww = 3;
                } else if (i == Cell.CELL_ABC) {
                    roww = 2;
                }
                // boolean rowOriented = ((TableModelForNonTabularData)
                // modelCell[i]).isRowOriented();
                int getaCol = 1;
                if (i == Cell.CELL_ABC) {
                    int icount = 0;
                    for (int j = 0; j < 2; j++) {
                        for (int k = 0; k < 3; k++) {
                            String ce = new String();
                            if (j == 0) {
                                ce = cellData[0][k];
                            } else {
                                ce = cellData[1][k];
                            }
                            if (ce != null) {
                                try {
                                    modelCell[i].setValueAt(formater
                                            .format(Double.parseDouble(ce)), 0,
                                            icount + getaCol);
                                } catch (NumberFormatException nfe) {
                                    logger.debug("invalid cell");
                                    continue;
                                }
                            }
                            icount++;
                        }
                    }
                } else if (i == Cell.CELL_VEC) {
                    for (int j = 0; j < 3; j++) {
                        for (int k = 0; k < 3; k++) {
                            String ce = cellData[j][k];
                            if (ce != null) {
                                try {
                                    modelCell[i].setValueAt(formater
                                            .format(Double.parseDouble(ce)), j,
                                            k + getaCol);
                                } catch (NumberFormatException nfe) {
                                    logger.debug("invalid cell");
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
        }

    }

    public String toString() {
        return atomList.toString();
    }

    public void debug() {
        for (int i = 0; i < atomList.size(); i++) {
            Atom atom = atomList.getAtomAt(i);
            String[] pos = atom.getPos();
            logger.debug("atom no. " + i + ": ");
            logger.debug("element name: " + atom.getElementName() + " x: "
                    + pos[0] + " y: " + pos[1] + " z: " + pos[2]);
            TaggedString[] auxil = atom.getAuxil();
            if (auxil != null) {
                for (int j = 0; j < auxil.length; j++) {
                    logger.debug("auxil info no " + j + ", ident: "
                            + auxil[j].getTag() + ", value: "
                            + auxil[j].getValue());
                }
            }
        }
        for (int i = 0; i < 2; i++) {
            Cell cell = this.cell[i];
            if (cell != null) {
                int cs = cell.getCellScheme();
                if (cs == Cell.CELL_VEC) {
                    String[][] cc = cell.getCell();
                    if (cc != null) {
                        logger.debug("cell scheme is CELL_VEC");
                        logger.debug("a-vec: " + cc[0][0] + " " + cc[0][1]
                                + " " + cc[0][2]);
                        logger.debug("b-vec: " + cc[1][0] + " " + cc[1][1]
                                + " " + cc[1][2]);
                        logger.debug("c-vec: " + cc[2][0] + " " + cc[2][1]
                                + " " + cc[2][2]);
                    }
                } else if (cs == Cell.CELL_ABC) {
                    String[][] cc = cell.getCell();
                    if (cc != null) {
                        logger.debug("cell scheme is CELL_ABC");
                        logger.debug("a: " + cc[0][0]);
                        logger.debug("b: " + cc[0][1]);
                        logger.debug("c: " + cc[0][2]);
                        logger.debug("alpha: " + cc[1][0]);
                        logger.debug("beta: " + cc[1][1]);
                        logger.debug("gamma: " + cc[1][2]);
                    }
                }
            } else {
                logger.debug("cell scheme no. " + i
                        + " has not been defined yet.");
            }
        }
    }

    /**
     * セルの表記法を変換
     * 
     * @param conv
     *            abc->vecの場合Cell.CELL_VECを, abc->vecの場合Cell.CELL_ABCを渡す
     */
    public boolean convertCell(int conv) {
        boolean ret = true;
        if (conv == Cell.CELL_VEC) {
            ret = abcToVec();
        } else if (conv == Cell.CELL_ABC) {
            ret = vecToABC();
        }
        updateTableCell();
        return ret;
    }

    private BravaisLattice blat;

    /**
     * 指定のオブジェクトから必要な情報を読み込み, BravaisLatticeを作成する.
     * 
     * @param inputInterface
     * @param primitive
     *            プリミティブセルの場合true. ヘンな作り方をする.
     */
    public void createBravaisLattice(InputInterface inputInterface,
            boolean primitive) {
        logger.debug("creating blat");
        if (cell == null && cell[0] == null && cell[1] == null) {
            logger.debug("cell is null.");
            return;
        }
        // if ( !BravaisLattice.isBravais(inputInterface) ) {
        // logger.debug("not bravais.");
        // return;
        // }

        if (!primitive) {
            if (cell[1] == null)
                convertCell(Cell.CELL_ABC);
            Cell cellabc = getCell(1);
            double[][] dcell = cellabc.getDouble();
            blat = BravaisLattice.getBravaisLattice(dcell[0][0], dcell[0][1],
                    dcell[0][2], dcell[1][0], dcell[1][1], dcell[1][2],
                    BravaisLattice.getLatticeSystemFrom(inputInterface));
            logger.debug("created blat.");
        } else {
            Cell vec = getCell(0);
            double[][] celvec = vec.getDouble();
            blat = BravaisLattice.getBravaisLattice(celvec[0], celvec[1],
                    celvec[2]);
            blat.isBravais(false);
        }
    }

    /**
     * 逆格子ベクトルを取得する. 失敗した場合nullを返す.
     * 
     * @param inputInterface
     *            Bravaisか否か, 格子の型などをこのオブジェクトから読み込む. 本来,
     *            このクラスにそういう情報を格納すべきかもしれない.
     * 
     * @return 逆格子ベクトル.
     */
    public double[][] getReciprocalLattice(InputInterface inputInterface) {
        if (cell == null && cell[0] == null && cell[1] == null)
            return null;

        double[][] rlat = null;
        createBravaisLattice(inputInterface, false);
        if (BravaisLattice.isBravais(inputInterface) && blat != null) {
            double[][] primlat = blat.getPrimitiveLatticeVector();
            Cell primcel = new Cell(primlat[0], primlat[1], primlat[2]);
            rlat = primcel.getReciprocalLattice();
        } else {
            Cell cell = this.cell[0];
            if (cell == null)
                cell = this.cell[1];
            rlat = cell.getReciprocalLattice();
        }
        return rlat;
    }

    /**
     * このAtomCoordsに関連付けられたBravaisLatticeを返す. ただしnullも有り得る!
     * 
     * @return このAtomCoordsに関連付けられたBravaisLattice
     */
    public BravaisLattice getBravaisLattice() {
        return blat;
    }

    /**
     * returns a value in angsrom units if the original one is not.
     */
    private double getAngstrom(double d) {
        if (unit == ANGSTROM) {
            return d;
        } else if (unit == BOHR || unit == DEFAULT) {
            return d * 0.529177d;
        } else if (unit == NM) {
            return d * 10.d;
        }
        logger.warn("wrong specification of unit!");
        return d;
    }

    private double getBohr(double d) {
        if (unit == ANGSTROM) {
            return d / 0.529177d;
        } else if (unit == BOHR || unit == DEFAULT) {
            return d;
        } else if (unit == NM) {
            return (d / 0.529177d) * 10.d;
        }
        logger.warn("wrong specification of unit!");
        return d;
    }

    private String[] distinctElements() {
        Vector elemReposit = new Vector();
        for (int i = 0; i < atomList.size(); i++) {
            boolean found = false;
            Atom atom = atomList.getAtomAt(i);
            String elem = atom.getElementName();
            for (int j = 0; j < elemReposit.size(); j++) {
                String compElem = (String) elemReposit.get(i);
                if (elem.equalsIgnoreCase(compElem)) {
                    found = true;
                    continue;
                }
            }
            if (!found) {
                elemReposit.addElement(elem);
            }
        }
        String[] ret = new String[elemReposit.size()];
        for (int i = 0; i < elemReposit.size(); i++) {
            ret[i] = (String) elemReposit.get(i);
        }

        return ret;
    }

    private boolean checkCell(int cellScheme) {
        if (cell == null || cell[cellScheme] == null
                || cell[cellScheme].getCell() == null) {
            return false;
        }

        String[][] cellString = cell[cellScheme].getCell();
        int numRows;
        if (cellScheme == Cell.CELL_VEC) {
            numRows = 3;
        } else {
            numRows = 2;
        }
        for (int i = 0; i < numRows; i++) {
            for (int j = 0; j < 3; j++) {
                try {
                    if (cellString[i][j] == null
                            || cellString[i][j].trim().length() == 0) {
                        return false;
                    }
                    Double.parseDouble(cellString[i][j]);
                } catch (NumberFormatException ne) {
                    logger.warn("not a valid cell.");
                    return false;
                } catch (NullPointerException nle) {
                    nle.printStackTrace();
                    logger.warn("cell not allocated");
                    return false;
                }
            }
        }

        return true;
    }

    private boolean abcToVec() {
        // if ( iscart ) {
        // logger.warn( "warning: can't convert from cellscheme2 to cellscheme1
        // in the case of 'Cartesian' coordinates");
        // return false;
        // }
        double[] cellLength = new double[3];
        double[] cellAng = new double[3];
        double alpha = 0.d;
        double beta = 0.d;
        double gamma = 0.d;

        String[][] cellData;
        try {
            cellData = cell[1].getCell();
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.warn("cell not allocated");
            return false;
        }

        try {
            for (int i = 0; i < 3; i++) {
                cellLength[i] = Double.parseDouble(cellData[0][i]);
            }
            alpha = Double.parseDouble(cellData[1][0])
                    * ConstParameters.ANG_TO_RAD;
            beta = Double.parseDouble(cellData[1][1])
                    * ConstParameters.ANG_TO_RAD;
            gamma = Double.parseDouble(cellData[1][2])
                    * ConstParameters.ANG_TO_RAD;
        } catch (Exception ex) {
            return false;
        }

        double[][] cellVec = new double[3][3];
        double temp = 0.;

        try {
            for (int i = 0; i < 3; ++i) {
                for (int j = 0; j < 3; ++j) {
                    cellVec[i][j] = 0.;
                }
            }
            cellVec[0][0] = 1.;
            cellVec[1][0] = Math.cos(gamma);
            cellVec[2][0] = Math.cos(beta);
            cellVec[1][1] = Math.sin(gamma);
            temp = (Math.cos(alpha) - Math.cos(beta) * Math.cos(gamma))
                    / Math.sin(gamma);
            cellVec[2][1] = temp;
            cellVec[2][2] = Math.sqrt(Math.sin(beta) * Math.sin(beta) - temp
                    * temp);
            for (int i = 0; i < 3; ++i) {
                for (int j = 0; j < 3; ++j) {
                    cellVec[j][i] *= cellLength[j];
                }
            }
        } catch (Exception e) {
            return false;
        }

        String[] avec = new String[3];
        String[] bvec = new String[3];
        String[] cvec = new String[3];

        try {
            for (int i = 0; i < 3; i++) {
                avec[i] = Double.toString(cellVec[0][i]);
                bvec[i] = Double.toString(cellVec[1][i]);
                cvec[i] = Double.toString(cellVec[2][i]);
            }
        } catch (Exception ex) {
            return false;
        }
        cell[0] = new Cell(avec, bvec, cvec);
        return true;
    }

    private boolean vecToABC() {
        boolean ret = true;
        String[][] cellData;
        try {
            cellData = cell[0].getCell();
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.warn("cell not allocated");
            return false;
        }

        String[] avec_out = cellData[0];
        String[] bvec_out = cellData[1];
        String[] cvec_out = cellData[2];

        double[] avec_double = new double[3];
        double[] bvec_double = new double[3];
        double[] cvec_double = new double[3];

        logger.debug("a_vec: " + avec_out[0] + " " + avec_out[1] + " "
                + avec_out[2]);
        logger.debug("b_vec: " + bvec_out[0] + " " + bvec_out[1] + " "
                + bvec_out[2]);
        logger.debug("c_vec: " + cvec_out[0] + " " + cvec_out[1] + " "
                + cvec_out[2]);

        try {
            for (int i = 0; i < 3; i++) {
                avec_double[i] = Double.parseDouble(avec_out[i]);
                bvec_double[i] = Double.parseDouble(bvec_out[i]);
                cvec_double[i] = Double.parseDouble(cvec_out[i]);
            }
        } catch (Exception ex) {
            return false;
        }

        double ar = 0.;
        double br = 0.;
        double cr = 0.;
        for (int i = 0; i < 3; i++) {
            ar += Math.pow(avec_double[i], 2);
            br += Math.pow(bvec_double[i], 2);
            cr += Math.pow(cvec_double[i], 2);
        }
        ar = Math.sqrt(ar);
        br = Math.sqrt(br);
        cr = Math.sqrt(cr);

        double cda = 0.;
        double cdb = 0.;
        double adb = 0.;

        for (int i = 0; i < 3; i++) {
            cda += cvec_double[i] * avec_double[i];
            cdb += cvec_double[i] * bvec_double[i];
            adb += avec_double[i] * bvec_double[i];
        }

        double cosbeta = 0.;
        double cosalph = 0.;
        double cosgamm = 0.;

        double beta = 0.;
        double alph = 0.;
        double gamm = 0.;

        try {
            cosbeta = cda / (cr * ar);
            cosalph = cdb / (cr * br);
            cosgamm = adb / (ar * br);

            beta = Math.acos(cosbeta) * ConstParameters.RAD_TO_ANG;
            alph = Math.acos(cosalph) * ConstParameters.RAD_TO_ANG;
            gamm = Math.acos(cosgamm) * ConstParameters.RAD_TO_ANG;
        } catch (Exception e) {
            return false;
        }

        try {
            String astr = Double.toString(ar);
            String bstr = Double.toString(br);
            String cstr = Double.toString(cr);
            String alstr = Double.toString(alph);
            String bestr = Double.toString(beta);
            String gastr = Double.toString(gamm);
            cell[1] = new Cell(astr, bstr, cstr, alstr, bestr, gastr);
            logger.debug("finished conversion. values of a,b,c,alpha,beta and gamma:");
            logger.debug(astr + " " + bstr + " " + cstr + " " + alstr + " "
                    + bestr + " " + gastr);
        } catch (Exception ex) {
            return false;
        }
        return true;
    }

}
