/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/01/20, 21:14
!  AUTHOR(S): KOGA, Junichiro
!  File : EnlargeCell.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.acviewer.operations;

import java.util.Vector;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.ConfigDataManager;
import ciss.phase_viewer.acviewer.ConfigDataUpdateEvent;
import ciss.phase_viewer.acviewer.MainPanel;
import ciss.phase_viewer.acviewer.scenegraphelements.TGAtom;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomObject;
import ciss.phase_viewer.atomcoord.Atom;
import ciss.phase_viewer.atomcoord.AtomCoords;
import ciss.phase_viewer.atomcoord.AtomList;
import ciss.phase_viewer.atomcoord.Cell;
import ciss.phase_viewer.atomcoord.VolumetricData;

/**
 * 「セルの拡大表示」を行うクラス.
 * 
 * @author
 */
public class EnlargeCell {
    private Logger logger = Logger.getLogger(EnlargeCell.class.getName());

    /* 「オリジナル」のセル・原子情報 */
    private Vector origCell = new Vector();

    private Vector origAtoms = new Vector();

    /* セルの倍率 */
    private int na = 1;

    private int nb = 1;

    private int nc = 1;

    private boolean doallframes = false;

    private Vector frames;

    private MainPanel parent;

    /**
     * @param frames
     *            AtomCoordsオブジェクトを格納したVector, 各要素が一つのフレームに相当する.
     *            通常は第一フレームにのみ作用する.
     * @param parent
     *            大元の画面
     */
    public EnlargeCell(Vector frames, MainPanel parent) {
        this.frames = frames;
        this.parent = parent;
        createOrig();
    }

    /**
     * コンストラクターに渡した全フレームに対して操作を行う場合, この値を真にする.
     * 
     * @param doallframes
     *            全フレームの処理を行う場合true
     */
    public void setDoAllFrames(boolean doallframes) {
        this.doallframes = doallframes;
    }

    /**
     * 拡大の倍率をセット. ただし, 元のセルからの倍率.
     * 
     * @param na
     *            a-軸の倍率
     * @param nb
     *            b-軸の倍率
     * @param nc
     *            c-軸の倍率
     */
    public void setEnlargeParams(int na, int nb, int nc) {
        this.na = na;
        this.nb = nb;
        this.nc = nc;
    }

    /**
     * 実際の「拡大操作」を行い, ConfigDataManagerに通知する. すると, 登録されたリスナーは
     * その変更を通知されるのでそこでまたしかるべき振る舞いをコードするとよい. 倍率の引数なしならセッターでセットされた値か,
     * もしくは全て'1'が採用される.
     */
    public void doEnlarge() {
        doEnlarge(na, nb, nc);
    }

    /**
     * 倍率を明示的に指定することも可能.
     * 
     * @param na
     *            a-軸の倍率
     * @param nb
     *            b-軸の倍率
     * @param nc
     *            c-軸の倍率
     */
    public void doEnlarge(int na, int nb, int nc) {
        ConfigDataManager mCD = parent.getCD();
        int start = 0;
        int end = origAtoms.size();
        int currentFrame = mCD.getFrameNum();
        if (!doallframes) {
            start = currentFrame;
            end = start + 1;
        }
        int orignumat = parent.getCD().getNumAt();
        parent.getCD().getAtomCoords().saveState();
        double[] com = new double[3];
        Vector sublatatoms = new Vector();
        for (int ii = start; ii < end; ii++) {
            logger.debug("doing 'enlarge' for frame no.: " + ii);
            double[][] dCell = new double[3][3];
            double[][] baseAtom = new double[3][3];
            Cell originalCell = (Cell) origCell.get(ii);
            Atom[] originalAtoms = (Atom[]) origAtoms.get(ii);
            double[][] dcell = originalCell.getDouble();
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    dCell[i][j] = dcell[i][j];
                }
            }

            if (originalAtoms == null) {
                logger.error("no atoms allocated.");
                return;
            }

            AtomCoords coords = (AtomCoords) frames.get(ii);
            AtomList atomList = coords.getAtomList();

            int orgnumat = atomList.size();
            for (int i = orgnumat - 1; i >= 0; i--) {
                atomList.removeAtomAt(i);
            }

            int newAtomCount = 0;
            double celloffsetx = (na - 1) * dCell[0][0] * 0.5 + (nb - 1)
                    * dCell[1][0] * 0.5 + (nc - 1) * dCell[2][0] * 0.5;
            double celloffsety = (na - 1) * dCell[0][1] * 0.5 + (nb - 1)
                    * dCell[1][1] * 0.5 + (nc - 1) * dCell[2][1] * 0.5;
            double celloffsetz = (na - 1) * dCell[0][2] * 0.5 + (nb - 1)
                    * dCell[1][2] * 0.5 + (nc - 1) * dCell[2][2] * 0.5;

            for (int iat = 0; iat < originalAtoms.length; iat++) {
                /* 同じセルのやつをまずやる */

                double[] origpos = originalAtoms[iat].getDouble();
                Atom newAtom = originalAtoms[iat].getCopy();
                newAtom.setPos(new double[] { origpos[0] - celloffsetx,
                        origpos[1] - celloffsety, origpos[2] - celloffsetz });
                // Atom newAtom = new Atom(originalAtoms[iat].getElementName(),
                // new double[] { origpos[0] - celloffsetx,
                // origpos[1] - celloffsety,
                // origpos[2] - celloffsetz }, originalAtoms[iat]
                // .getAuxil());
                newAtomCount++;
                atomList.addAtom(newAtom, true);
            }

            /* そのほかのセルのやつ */
            for (int ia = 0; ia < na; ia++) {
                for (int ib = 0; ib < nb; ib++) {
                    for (int ic = 0; ic < nc; ic++) {
                        if (ia == 0 && ib == 0 && ic == 0)
                            continue;
                        for (int iat = 0; iat < originalAtoms.length; iat++) {
                            double[] origpos = originalAtoms[iat].getDouble();
                            double xtmp = origpos[0] + (double) ia
                                    * dCell[0][0] + (double) ib * dCell[1][0]
                                    + (double) ic * dCell[2][0] - celloffsetx;
                            double ytmp = origpos[1] + (double) ia
                                    * dCell[0][1] + (double) ib * dCell[1][1]
                                    + (double) ic * dCell[2][1] - celloffsety;
                            double ztmp = origpos[2] + (double) ia
                                    * dCell[0][2] + (double) ib * dCell[1][2]
                                    + (double) ic * dCell[2][2] - celloffsetz;

                            Atom newAtom = originalAtoms[iat].getCopy();
                            newAtom.setPos(new double[] { xtmp, ytmp, ztmp });
                            // Atom newAtom = new Atom(originalAtoms[iat]
                            // .getElementName(), new double[] { xtmp,
                            // ytmp, ztmp }, originalAtoms[iat].getAuxil());

                            newAtomCount++;
                            atomList.addAtom(newAtom, true);
                        }
                    }
                }
            }

            double[][] newCell = new double[3][3];
            for (int i = 0; i < 3; i++) {
                newCell[0][i] = (double) na * dCell[0][i];
                newCell[1][i] = (double) nb * dCell[1][i];
                newCell[2][i] = (double) nc * dCell[2][i];
            }
            logger.debug("newcell: " + newCell[0][0] + ", " + newCell[0][1]
                    + ", " + newCell[0][2]);
            logger.debug("newcell: " + newCell[1][0] + ", " + newCell[1][1]
                    + ", " + newCell[1][2]);
            logger.debug("newcell: " + newCell[2][0] + ", " + newCell[2][1]
                    + ", " + newCell[2][2]);
            Cell newcell = new Cell(newCell[0], newCell[1], newCell[2]);
            newcell.setCellOffset(new double[] { celloffsetx, celloffsety,
                    celloffsetz });
            coords.setCell(newcell);
            VolumetricData[] cden = coords.getChargeDensities();
            if (cden != null) {
                for (int i = 0; i < cden.length; i++) {
                    cden[i].enlarge(na, nb, nc);
                }
            }
            atomList.setUndoCount(orgnumat + newAtomCount + 1);
        }
        try {
            AtomList list = parent.getCD().getAtomCoords().getAtomList();
            logger.debug("size: " + list.size());
            logger.debug("new COM: " + com[0] + ", " + com[1] + ", " + com[2]);

            parent.getCD().isRenderingManual(true);
            parent.prepareRedraw();
            int[] sele = ((TGAtom) parent.getRootTransform())
                    .getSelectedAtomsIndex();
            ((TGAtom) parent.getRootTransform()).deselectAll();
            parent.getCD().setCoords((AtomCoords) frames.get(currentFrame),
                    ConfigDataUpdateEvent.CELL_CHANGED);
            AtomObject[] objs = ((TGAtom) parent.getRootTransform())
                    .getAtomObjects();
            if (sele != null && objs != null) {
                for (int i = 0; i < sele.length; i++) {
                    if (sele[i] < objs.length) {
                        objs[sele[i]].setSelected();
                    }
                }
            }
            // double scale =
            // Math.pow((double)orignumat/((double)list.size()),1d/3d);
            // Transform3D sc = new Transform3D();
            // sc.setScale(scale);
            // parent.getScene().getRootRootTransform().setTransform(sc);
            parent.doUnitCell(true);
            parent.postRedraw();
            parent.getCD().isRenderingManual(false);
            parent.getCD().getAtomCoords().finalizeState();
        } catch (Exception exc) {
            exc.printStackTrace();
        }

    }

    public void recreateOrig() {
        ConfigDataManager mCD = parent.getCD();

        AtomCoords coords = parent.getCD().getAtomCoords();
        AtomList atomList = coords.getAtomList();
        int numOrgAt = atomList.size();
        Vector tmpvec = new Vector();
        for (int i = 0; i < numOrgAt; i++) {
            Atom tmp = atomList.getAtomAt(i);
            if (tmp.isBoundaryAtom())
                continue;
            // Atom buf = new Atom(tmp.getElementName(), tmp.getPos(), tmp
            // .getAuxil());
            Atom buf = tmp.getCopy();
            tmpvec.add(buf);
        }
        if (tmpvec.size() == 0) {
            return;
        }

        Atom[] oatoms = new Atom[tmpvec.size()];
        tmpvec.copyInto(oatoms);

        Cell ocell = coords.getCell(0);
        origAtoms.set(mCD.getFrameNum(), oatoms);
        origCell.set(mCD.getFrameNum(), ocell);
    }

    private void createOrig() {
        for (int i = 0; i < frames.size(); i++) {
            AtomCoords coords = (AtomCoords) frames.get(i);
            AtomList atomList = coords.getAtomList();
            int numOrgAt = atomList.size();
            Vector tmpvec = new Vector();
            for (int j = 0; j < numOrgAt; j++) {
                Atom tmp = atomList.getAtomAt(j);
                // if (tmp.isBoundaryAtom() || tmp.isSubLatticeAtom())
                if (tmp.isBoundaryAtom())
                    continue;
                // Atom buf = new Atom(tmp.getElementName(), tmp.getPos(), tmp
                // .getAuxil());
                Atom buf = tmp.getCopy();
                tmpvec.addElement(buf);
            }
            if (tmpvec.size() == 0) {
                return;
            }

            Atom[] originalAtoms = new Atom[tmpvec.size()];
            tmpvec.copyInto(originalAtoms);

            Cell originalCell = coords.getCell(0);
            origAtoms.addElement(originalAtoms);
            origCell.addElement(originalCell);
        }
    }

}
