/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : CellObject.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.scenegraphelements;

import java.util.Vector;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Material;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.ACVData;
import ciss.phase_viewer.acviewer.ConfigDataUpdateEvent;
import ciss.phase_viewer.acviewer.CoordsViewerInterface;
import ciss.phase_viewer.acviewer.MainPanel;
import ciss.phase_viewer.acviewer.fbz.WignerSeitzAttributes;
import ciss.phase_viewer.acviewer.fbz.WignerSeitzCell;
import ciss.phase_viewer.atomcoord.symmetry.BravaisLattice;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;

public class CellObject extends SceneGraphElementBG {
    private Logger logger = Logger.getLogger(CellObject.class.getName());
    private double[] cellOrigin;
    private double[][] cellVec;
    private double[] cellOffset;

    private double[][] CellTyo;

    private int numNodes = 30;
    private float cellWidth = 0.005f;

    public CellObject() {
        super();
    }

    public int getType() {
        return SceneGraphElement.CELL;
    }

    private WignerSeitzCell wscell;

    public void createWSCell() {
        Point3f avec = new Point3f();
        Point3f bvec = new Point3f();
        Point3f cvec = new Point3f();
        Point3f origin = new Point3f();
        double lenmax = (float) mCD.getLenMax();
        avec.x = (float) (cellVec[0][0] / lenmax);
        avec.y = (float) (cellVec[0][1] / lenmax);
        avec.z = (float) (cellVec[0][2] / lenmax);

        bvec.x = (float) (cellVec[1][0] / lenmax);
        bvec.y = (float) (cellVec[1][1] / lenmax);
        bvec.z = (float) (cellVec[1][2] / lenmax);

        cvec.x = (float) (cellVec[2][0] / lenmax);
        cvec.y = (float) (cellVec[2][1] / lenmax);
        cvec.z = (float) (cellVec[2][2] / lenmax);

        WignerSeitzAttributes wsattrs = new WignerSeitzAttributes();
        wsattrs.isReciprocalSpace = false;
        wscell = new WignerSeitzCell(avec, bvec, cvec, wsattrs);
        this.addChild(wscell);
    }

    /**
     * このセルに関連付けられたWSCellを取得
     * 
     * @return Wigner-Seitz Cell.
     */
    public WignerSeitzCell getWSCell() {
        return this.wscell;
    }

    /**
     * このセルに関連付けられたWSCellをnullに.
     */
    public void nullifyWSCell() {
        this.wscell = null;
    }

    public void recreate(double[] cellOrigin, double[][] cellVec) {
    }

    protected void create() {
        create(false);
    }

    boolean recreate = false;

    protected void create(boolean recreate) {
        this.recreate = recreate;
        double lenmax = mCD.getLenMax();

        this.cellOrigin = mCD.getCellOriginVector(mACVD.getShiftPolicy());

        this.cellVec = mCD.getCell();
        if (this.cellVec == null)
            return;

        CellTyo = new double[8][3];

        logger.debug("creating cell");
        logger.debug("cellvec:");
        logger.debug("avec: " + cellVec[0][0] + " " + cellVec[0][1] + " "
                + cellVec[0][2]);
        logger.debug("bvec: " + cellVec[1][0] + " " + cellVec[1][1] + " "
                + cellVec[1][2]);
        logger.debug("cvec: " + cellVec[2][0] + " " + cellVec[2][1] + " "
                + cellVec[2][2]);

        double[] offset = parent.getCellOffset();
        //
        // Transform3D [] t3dbuff=null;
        // if ( transforms!=null ) {
        // t3dbuff = new Transform3D[transforms.size()];
        // TransformGroup[] tgs = getTransforms();
        // Vector3d vec = new Vector3d();
        // t3dbuff[0] = new Transform3D();
        // tgs[0].getTransform(t3dbuff[0]);
        // t3dbuff[0].get(vec);
        // logger.debug("vec: "+vec.x+", "+vec.y+", "+vec.z);
        // offset[0] = vec.x;
        // offset[1] = vec.y;
        // offset[2] = vec.z;
        // }

        removeAllChildren();

        double[] oo = new double[3];
        double[][] celve = new double[3][3];
        for (int i = 0; i < 3; i++) {
            oo[i] = cellOrigin[i] / lenmax + offset[i];
            for (int j = 0; j < 3; j++) {
                celve[i][j] = cellVec[i][j] / lenmax;
            }
        }

        CellTyo = getVertices(oo, celve);

        transforms = new Vector();

        TransformGroup t = new TransformGroup(); // まっさらなTransformを登録しておく
        t.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        t.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        t.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
        transforms.add(t);

        float[] col = mACVD.getCellColor();

        addChild(createCell(0, 1, CellTyo, col));
        addChild(createCell(0, 2, CellTyo, col));
        addChild(createCell(1, 3, CellTyo, col));
        addChild(createCell(2, 3, CellTyo, col));

        addChild(createCell(4, 6, CellTyo, col));
        addChild(createCell(4, 5, CellTyo, col));
        addChild(createCell(6, 7, CellTyo, col));
        addChild(createCell(5, 7, CellTyo, col));

        addChild(createCell(0, 4, CellTyo, col));
        addChild(createCell(2, 6, CellTyo, col));
        addChild(createCell(1, 5, CellTyo, col));
        addChild(createCell(3, 7, CellTyo, col));
    }

    private double[][] getVertices(double[] origin, double[][] vector) {
        double[][] ret = new double[8][3];
        for (int i = 0; i < 3; i++) {
            ret[0][i] = origin[i];
        }

        for (int i = 0; i < 3; i++) {
            ret[1][i] = vector[0][i] + ret[0][i];
        }

        for (int i = 0; i < 3; i++) {
            ret[2][i] = vector[1][i] + ret[0][i];
        }

        for (int i = 0; i < 3; i++) {
            ret[3][i] = (vector[0][i] + vector[1][i]) + ret[0][i];
        }

        for (int i = 0; i < 3; i++) {
            ret[4][i] = vector[2][i] + ret[0][i];
        }

        for (int i = 0; i < 3; i++) {
            ret[5][i] = (vector[0][i] + vector[2][i]) + ret[0][i];
        }

        for (int i = 0; i < 3; i++) {
            ret[6][i] = (vector[1][i] + vector[2][i]) + ret[0][i];
        }

        for (int i = 0; i < 3; i++) {
            ret[7][i] = (vector[1][i] + vector[2][i] + vector[0][i])
                    + ret[0][i];
        }

        return ret;
    }

    private BranchGroup primitiveCell;

    public void addPrimitiveCell() {
        if (!(mACVD.getParent() instanceof MainPanel))
            return;

        ACVData data = ((CoordsViewerInterface) mACVD.getParent()).getData();
        BravaisLattice bl = data.getBravaisLattice();
        if (bl == null)
            return;

        double[][] cell = bl.getPrimitiveLatticeVector();

        double[] ori = mCD.getCellOriginVector(mACVD.getShiftPolicy());
        double[] off = parent.getCellOffset();
        double[] tmpori = new double[3];
        for (int i = 0; i < 3; i++)
            tmpori[i] = ori[i] / mCD.getLenMax() + off[i];
        double[][] tmpvec = new double[3][3];
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                tmpvec[i][j] = cell[i][j] / mCD.getLenMax();

        double[][] vertices = getVertices(tmpori, tmpvec);

        if (primitiveCell != null)
            primitiveCell.detach();
        primitiveCell = new BranchGroup();
        primitiveCell.setCapability(BranchGroup.ALLOW_DETACH);

        GlobalProperties props = PropertiesManager
                .getGlobalProperties(PropertiesManager.PROPERTIES_ACV);
        float[] col = new float[] { 0, 0, 0 };
        try {
            col[0] = Float.parseFloat(props
                    .getProperty("primitive_cell_color_r"));
            col[1] = Float.parseFloat(props
                    .getProperty("primitive_cell_color_g"));
            col[2] = Float.parseFloat(props
                    .getProperty("primitive_cell_color_b"));
        } catch (NumberFormatException nfe) {
        } catch (ArrayIndexOutOfBoundsException aob) {
        }

        primitiveCell.addChild(createCell(0, 1, vertices, col));
        primitiveCell.addChild(createCell(0, 2, vertices, col));
        primitiveCell.addChild(createCell(1, 3, vertices, col));
        primitiveCell.addChild(createCell(2, 3, vertices, col));

        primitiveCell.addChild(createCell(4, 6, vertices, col));
        primitiveCell.addChild(createCell(4, 5, vertices, col));
        primitiveCell.addChild(createCell(6, 7, vertices, col));
        primitiveCell.addChild(createCell(5, 7, vertices, col));

        primitiveCell.addChild(createCell(0, 4, vertices, col));
        primitiveCell.addChild(createCell(2, 6, vertices, col));
        primitiveCell.addChild(createCell(1, 5, vertices, col));
        primitiveCell.addChild(createCell(3, 7, vertices, col));

        addChild(primitiveCell);
    }

    public void removePrimitiveCell() {
        if (primitiveCell != null)
            primitiveCell.detach();
    }

    public void configDataUpdate() {
        logger.debug("at configDataUpdate()");
        if (cellVec == null) {
            logger.debug("cellvec is null...");
            return;
        }
        double[][] cellVec_tmp = mCD.getCell();
        if (cellVec_tmp == null) {
            return;
        }
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (cellVec[i][j] != cellVec_tmp[i][j]) {
                    create(true);
                    return;
                }
            }
        }
    }

    public void configDataUpdate(boolean foo, ConfigDataUpdateEvent e) {
        configDataUpdate();
    }

    public boolean needsUpdate() {
        return true;
    }

    protected void setNumNodes(int numNodes) {
        this.numNodes = numNodes;
    }

    protected void setCellWidth(float cellWidth) {
        this.cellWidth = cellWidth;
    }

    private Vector transforms;

    private BranchGroup createCell(int i1, int i2, double[][] vertices,
            float[] cellcolor) {
        double[] CellHen = new double[6];
        for (int i = 0; i < 3; i++) {
            CellHen[i] = vertices[i1][i];
            CellHen[i + 3] = vertices[i2][i];
        }

        Appearance app = new Appearance();
        Material material = new Material();
        material.setDiffuseColor(new Color3f(cellcolor));
        material.setShininess(120.0f);
        app.setMaterial(material);

        Point3d Cyl1 = new Point3d(CellHen[0], CellHen[1], CellHen[2]);
        Point3d Cyl2 = new Point3d(CellHen[3], CellHen[4], CellHen[5]);

        CylinderCreatorTG cylindercreateTG = new CylinderCreatorTG(Cyl1, Cyl2);
        cylindercreateTG.setColor(new Color3f(cellcolor));
        cylindercreateTG.setRadius(mACVD.getCellWidth());
        cylindercreateTG.setResolution(mACVD.getNumNodesCell());
        cylindercreateTG.setMode(CylinderCreatorTG.CYLINDER);
        if (mACVD.getUseWireForCell()) {
            cylindercreateTG.setMode(CylinderCreatorTG.WIRE);
        }
        cylindercreateTG.create();
        BranchGroup cylinder = new BranchGroup();
        cylinder.setCapability(BranchGroup.ALLOW_DETACH);
        cylinder.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        cylinder.addChild(cylindercreateTG);

        transforms.add(cylindercreateTG);

        return cylinder;
    }

    public TransformGroup[] getTransforms() {
        TransformGroup[] ret = new TransformGroup[transforms.size()];
        transforms.copyInto(ret);
        return ret;
    }

}
