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

import java.awt.Component;
import java.awt.Container;
import java.awt.event.KeyListener;

import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JPanel;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.keylistener.J3DKeyListener;
import ciss.phase_viewer.acviewer.mouselistener.MyMouseRotate;
import ciss.phase_viewer.acviewer.mouselistener.MyMouseTranslate;
import ciss.phase_viewer.acviewer.scenegraphelements.SceneGraphElement;
import ciss.phase_viewer.acviewer.scenegraphelements.SceneGraphElementCreator;
import ciss.phase_viewer.acviewer.scenegraphelements.TGAtom;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomPicker;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;

import com.sun.j3d.utils.behaviors.mouse.MouseWheelZoom;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;

public class Show3D implements ConfigData {

    private static Logger logger = Logger.getLogger(Show3D.class.getName());

    private JPanel panel;

    private MainPanel mp;

    private J3DDataManager mACVD;

    private ConfigDataManager mCD;

    private Container cont;

    private int NumAt;

    private int i;

    private int NumBond;

    private TGAtom tgatom;

    private TransformGroup objTransformAxis;

    private Transform3D trans3d;

    private Transform3D trans3dAxis;

    private Transform3D translateorigin;

    private int numNewElements = 0;

    private int lastNumAt;

    private int[] selectedAts;

    /**
     * まずACVDataManagerやConfigDataManagerへの参照を取得し, 必要な TransformGroupなどを作成する.
     * 
     * @param parent
     *            呼び出し元のGUI
     */
    public Show3D(MainPanel parent) {
        this.mp = parent;
        this.mACVD = parent.getJ3DDataManager();
        this.mCD = parent.getCD();
        if (mCD != null)
            this.mCD.register(this);

        tgatom = new TGAtom();

        GlobalProperties props = PropertiesManager
                .getGlobalProperties(PropertiesManager.PROPERTIES_ACV);
        Vector3f pos = new Vector3f(-0.8f, -0.7f, 0.0f);
        try {
            String[] spos = props.getProperty("axis_pos").split(",");
            pos = new Vector3f(Float.parseFloat(spos[0]),
                    Float.parseFloat(spos[1]), Float.parseFloat(spos[2]));
        } catch (Exception exc) {
            exc.printStackTrace();
        }

        translateorigin = new Transform3D();
        translateorigin.set(pos);
        objTransformAxis = new TransformGroup(translateorigin);
        objTransformAxis.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        objTransformAxis.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        objTransformAxis.setCapability(TransformGroup.ALLOW_CHILDREN_READ);

        trans3d = new Transform3D();
        trans3dAxis = new Transform3D();
        objTransformAxis.getTransform(trans3dAxis);
    }

    /**
     * 参照をなるべく解放すべく努力する
     */
    public void cleanUp() {
        objRoot = null;
        creator = null;
        tgatom = null;
        objTransformAxis = null;
        mACVD = null;
        mCD = null;
    }

    /**
     * ACVDataManagerへの参照をセットする.
     * 
     * @param mACVD
     *            原子配置表示に関わる情報を保持するクラス.
     */
    public void setmACVD(J3DDataManager mACVD) {
        this.mACVD = mACVD;
    }

    /**
     * このクラスが持っているACVDataManagerへの参照を返す.
     * 
     * @return 原子配置表示に関わる情報を保持するクラス.
     */
    public J3DDataManager getACV() {
        return this.mACVD;
    }

    /**
     * このクラスが持っているConfigDataManagerへの参照を返す.
     * 
     * @return 原子配置データを保持するクラス.
     */
    public ConfigDataManager getCD() {
        return this.mCD;
    }

    /**
     * 前回の表示を保持し, 新たにTransformGroupを初期化する.
     */
    public void storeState() {
        storeTrans();
        storeSelected();
        initTGAtom();
    }

    /**
     * 再描画の前に, Scene Graphに対しどのくらい回転・並進・縮小操作が 行われたかを保存しておくメソッド. 再描画の前に呼ばないと上記の
     * マウスによる変更が反映されない.
     */
    private void storeTrans() {
        tgatom.getTransform(trans3d);
        objTransformAxis.getTransform(trans3dAxis);
    }

    /**
     * 「選択中の原子」の情報を保存
     */
    private void storeSelected() {
        lastNumAt = tgatom.getLastNumAt();
        selectedAts = tgatom.getSelectedAtomsIndex();
    }

    /**
     * 原子と軸のTransformGroupを初期化する. 前回の状態をがっつり保持した後 呼ぶ.
     */
    private void initTGAtom() {
        tgatom = new TGAtom();
        tgatom.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
        tgatom.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        tgatom.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        tgatom.setCapability(TransformGroup.ENABLE_PICK_REPORTING);

        objTransformAxis = new TransformGroup(translateorigin);
        objTransformAxis.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        objTransformAxis.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        objTransformAxis.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
    }

    /**
     * 新元素登録クラス(NewElementCreater)を呼ぶ.
     */
    public void createNewElement() {
        NewElementCreater creater = new NewElementCreater(mACVD, mCD);
        numNewElements = creater.createNewElement();
    }

    /**
     * 原子配置のオブジェクトを作成し, それを返す.
     * 
     * @return 原子配置のBranchGroup
     */
    public BranchGroup getObjRoot() {
        return createObjRoot();
    }

    /**
     * シーングラフを作成した後, さらにポスト処理をするメソッド.
     */
    protected void doPost() {
        tgatom.restoreSelection(lastNumAt, selectedAts);
    }

    private BranchGroup rootRootBranchGroup;

    private BranchGroup rootRootRootBranchGroup;

    private TransformGroup rootRootTransform;

    /**
     * TGAtomには, さらにもう一つ上のTransformGroupをつけている. そのTransformGroupを返す
     * 
     * @return 上記の通り
     */
    public TransformGroup getRootRootTransform() {
        return this.rootRootTransform;
    }

    private BranchGroup objRoot;

    private MyMouseRotate myMouseRotate;

    private BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0,
            0.0), 100.0);

    private BranchGroup legendBranch;

    private BranchGroup axisBranch;

    private SceneGraphElementCreator creator;

    public void setMouseMotionBehaviours() {
        Component component = mp.getJCanvas3D();

        if (BaseJ3DPanel.useJCanvas3D())
            myMouseRotate = new MyMouseRotate(component);
        else
            myMouseRotate = new MyMouseRotate();

        myMouseRotate.setTransformGroup(tgatom);
        // myMouseRotate.setTransformGroup(rootRootTransform);
        myMouseRotate.setSchedulingBounds(bounds);
        // myMouseRotate.setTranslation(new Vector3d(1.1d, 0.1d, 0.d));
        // objRoot.addChild(myMouseRotate);
        objRoot.addChild(myMouseRotate);

        MyMouseTranslate myMouseTranslate = null;
        if (BaseJ3DPanel.useJCanvas3D())
            myMouseTranslate = new MyMouseTranslate(component);
        else
            myMouseTranslate = new MyMouseTranslate();

        myMouseTranslate.setTransformGroup(tgatom);
        // myMouseTranslate.setTransformGroup(rootRootTransform);
        myMouseTranslate.setSchedulingBounds(bounds);
        objRoot.addChild(myMouseTranslate);

        MouseZoom myMouseZoom = null;
        if (BaseJ3DPanel.useJCanvas3D())
            myMouseZoom = new MouseZoom(component);
        else
            myMouseZoom = new MouseZoom();

        myMouseZoom.setTransformGroup(tgatom);
        // myMouseZoom.setTransformGroup(rootRootTransform);
        myMouseZoom.setSchedulingBounds(bounds);
        objRoot.addChild(myMouseZoom);

        MouseWheelZoom myMouseWheelZoom = null;
        if (BaseJ3DPanel.useJCanvas3D())
            myMouseWheelZoom = new MouseWheelZoom(component);
        else
            myMouseWheelZoom = new MouseWheelZoom();
        myMouseWheelZoom.setTransformGroup(tgatom);
        myMouseWheelZoom.setSchedulingBounds(bounds);
        objRoot.addChild(myMouseWheelZoom);
    }

    private BranchGroup createObjRoot() {
        creator = new SceneGraphElementCreator(mACVD, mCD);

        objRoot = new BranchGroup();

        objRoot.setCapability(BranchGroup.ALLOW_DETACH);
        objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

        AtomPicker picker = new AtomPicker(mACVD.getParent().getCanvas(),
                objRoot, new BoundingSphere());
        tgatom.setPicker(picker);
        tgatom.setSceneGraphElementCreator(creator);
        creator.setTGAtom(tgatom);
        tgatom.setParentFrame(mp);

        tgatom.create();
        tgatom.setTransform(trans3d);

        rootRootTransform = new TransformGroup();
        rootRootTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        rootRootTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        rootRootTransform.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
        rootRootTransform.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
        rootRootTransform.addChild(tgatom);

        objRoot.addChild(rootRootTransform);

        Component component = mp.getJCanvas3D();
        //
        // if (BaseJ3DPanel.useJCanvas3D())
        // myMouseRotate = new MyMouseRotate(component);
        // else
        // myMouseRotate = new MyMouseRotate();
        //
        // myMouseRotate.setTransformGroup(tgatom);
        // // myMouseRotate.setTransformGroup(rootRootTransform);
        // myMouseRotate.setSchedulingBounds(bounds);
        // myMouseRotate.setTranslation(new Vector3d(1.1d, 0.1d, 0.d));
        // // objRoot.addChild(myMouseRotate);
        // objRoot.addChild(myMouseRotate);
        //
        // MyMouseTranslate myMouseTranslate = null;
        // if (BaseJ3DPanel.useJCanvas3D())
        // myMouseTranslate = new MyMouseTranslate(component);
        // else
        // myMouseTranslate = new MyMouseTranslate();
        //
        // myMouseTranslate.setTransformGroup(tgatom);
        // // myMouseTranslate.setTransformGroup(rootRootTransform);
        // myMouseTranslate.setSchedulingBounds(bounds);
        // objRoot.addChild(myMouseTranslate);
        //
        // MouseZoom myMouseZoom = null;
        // if (BaseJ3DPanel.useJCanvas3D())
        // myMouseZoom = new MouseZoom(component);
        // else
        // myMouseZoom = new MouseZoom();
        //
        // myMouseZoom.setTransformGroup(tgatom);
        // // myMouseZoom.setTransformGroup(rootRootTransform);
        // myMouseZoom.setSchedulingBounds(bounds);
        // objRoot.addChild(myMouseZoom);
        //
        // MouseWheelZoom myMouseWheelZoom = null;
        // if (BaseJ3DPanel.useJCanvas3D())
        // myMouseWheelZoom = new MouseWheelZoom(component);
        // else
        // myMouseWheelZoom = new MouseWheelZoom();
        // myMouseWheelZoom.setTransformGroup(tgatom);
        // myMouseWheelZoom.setSchedulingBounds(bounds);
        // objRoot.addChild(myMouseWheelZoom);

        objTransformAxis = creator.getSceneGraphElement(SceneGraphElement.AXIS);

        objTransformAxis.setTransform(trans3dAxis);
        MyMouseRotate myMouseRotateAxis = null;
        if (BaseJ3DPanel.useJCanvas3D())
            myMouseRotateAxis = new MyMouseRotate(component);
        else
            myMouseRotateAxis = new MyMouseRotate();

        myMouseRotateAxis.setTransformGroup(objTransformAxis);
        myMouseRotateAxis.setSchedulingBounds(bounds);
        myMouseRotateAxis.setTranslation(new Vector3d(0.5, 0.0, 0.0));
        objRoot.addChild(myMouseRotateAxis);

        // get legend ( if legend==true )
        if (mACVD.getLegend()) {
            legendBranch = new BranchGroup();
            legendBranch.setCapability(BranchGroup.ALLOW_DETACH);
            legendBranch.addChild(creator
                    .getSceneGraphElement(SceneGraphElement.LEGEND));
            objRoot.addChild(legendBranch);
        }

        axisBranch = new BranchGroup();
        axisBranch.setCapability(BranchGroup.ALLOW_DETACH);
        axisBranch.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        axisBranch.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        axisBranch.addChild(objTransformAxis);
        if (mACVD.getAxis()) {
            objRoot.addChild(axisBranch);
        }

        // back ground color
        objRoot.addChild(creator.getSceneGraphElement(SceneGraphElement.BG));
        // light
        objRoot.addChild(creator.getSceneGraphElement(SceneGraphElement.LIGHT));

        // キーリスナーを登録
        KeyListener[] listeners = J3DKeyListener.getKeyListeners(mp);
        for (int i = 0; i < listeners.length; i++) {
            if (BaseJ3DPanel.useJCanvas3D())
                mp.getJCanvas3D().addKeyListener(listeners[i]);
            else
                mp.getCanvas().addKeyListener(listeners[i]);
        }
        return objRoot;
    }

    public TransformGroup getTGAtom() {
        return (TransformGroup) tgatom;
    }

    public TransformGroup getTGAxis() {
        return objTransformAxis;
    }

    public void setTransform(Transform3D transform) {
        Transform3D at = new Transform3D();
        tgatom.getTransform(at);
        at.mul(transform);
        tgatom.setTransform(at);

        Transform3D axi = new Transform3D();
        objTransformAxis.getTransform(axi);
        axi.mul(transform);
        objTransformAxis.setTransform(axi);
    }

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

    public boolean needsUpdate() {
        return true;
    }

    public void configDataUpdate() {
        if (legendBranch != null)
            legendBranch.detach();
        if (mACVD.getLegend()) {
            legendBranch = new BranchGroup();
            legendBranch.setCapability(BranchGroup.ALLOW_DETACH);
            legendBranch.addChild(creator
                    .getSceneGraphElement(SceneGraphElement.LEGEND));
            objRoot.addChild(legendBranch);
        }

        if (axisBranch != null)
            axisBranch.detach();
        if (mACVD.getAxis()) {
            objRoot.addChild(axisBranch);
        }
    }

}
