/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/07/18, 20:37
!  AUTHOR(S): KOGA, Junichiro
!  File : Light3DPanel.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.test;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DepthComponentFloat;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.GraphicsContext3D;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.VirtualUniverse;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.vecmath.Point3f;

import org.apache.log4j.Logger;
import org.freehep.util.export.ExportDialog;

import ciss.phase_viewer.acviewer.ChaseTransformGroup;
import ciss.phase_viewer.acviewer.J3DDataManager;
import ciss.phase_viewer.acviewer.J3DPanel;
import ciss.phase_viewer.acviewer.PluginParser;
import ciss.phase_viewer.acviewer.vrml.VRMLExportFileType;
import ciss.phase_viewer.acviewer.vrml.VRMLExporter;
import ciss.phase_viewer.atomcoord.VolumetricData;
import ciss.phase_viewer.common.Drawable;
import ciss.phase_viewer.graph.export.PDFExportFileType;
import ciss.phase_viewer.graph.export.SVGExportFileType;
import ciss.phase_viewer.mainpanel.InternalFrameChase;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;

import com.sun.j3d.exp.swing.JCanvas3D;
import com.sun.j3d.utils.universe.SimpleUniverse;

/**
 * Java3Dで三次元空間を描画するための, ベースクラス. 今のところ, 原子配置ビューアーとブリユアンゾーンビューアーに 利用されている.
 * 
 * @author
 */
public abstract class Light3DPanel extends InternalFrameChase implements
        Drawable, J3DPanel {
    private Logger logger = Logger.getLogger(Light3DPanel.class.getName());

    /** 描画される, 一番上のBranchGroup */
    protected BranchGroup rootBranch;

    /** 利用されるCanvas3D */
    protected Canvas3D canvas3d;

    /** 利用されるSimpleUniverse; より複雑なUniverseを利用する場合は別途作成 */
    protected SimpleUniverse simpleU;

    /** 関連付けられたGUIを格納するためのVector */
    protected Vector associatedGUIs = new Vector();

    /** エクスポート時に利用する */
    protected JLabel exportLabel;

    /** プラグインを処理するためのオブジェクト */
    protected PluginParser pluginParser;

    /** ベースとなるべきディレクトリー. エクスポートの際などに利用される */
    protected String parentDir = System.getProperty("user.home");

    /** 描画に関わる, いくつかの情報を保持するオブジェクト */
    protected J3DDataManager dataManager;

    /** 再描画を行うべきか否かを判定するためのフラグ */
    protected boolean doRedraw = true;

    /** ビューアー用のプロパティー */
    protected GlobalProperties prop = PropertiesManager
            .getGlobalProperties(PropertiesManager.PROPERTIES_ACV);

    /* 大元のJPanelのようなもの */
    protected JCanvas3D rootPanel;

    /**
     * ルートフレームのタイトルとその大きさを指定する.
     * 
     * @param title
     *            ルートフレームのタイトル
     * @param initSize
     *            ルートフレームの大きさ
     */
    public Light3DPanel(String title, Dimension initSize) {
        super(title, initSize);
        initBaseJ3DPanel();
    }

    /**
     * プラグイン情報の記述されたファイルへのパスを返す; デフォルトは原子配置ビューアーのそれ
     * 
     * @return プラグイン情報の記述されたファイルへのパス
     */
    protected String getPluginPath() {
        return PluginParser.PLUGIN_ACV;
    }

    private void initBaseJ3DPanel() {
        // addWindowListener(new WindowAdapter() {
        // public void windowClosing(WindowEvent we) {
        // destroy();
        // }
        //
        // public void windowClosed(WindowEvent we) {
        // destroy();
        // }
        // });
        // GraphicsConfiguration gc = null;
        // int retries=0;
        // while ( gc == null || retries < 100 ) {
        // gc = SimpleUniverse.getPreferredConfiguration();
        // retries++;
        // }
        // if ( gc == null ) {
        // logger.error("falied to obtain a suitable graphics configuration... retry.");
        // dispose();
        // return;
        // }
        // logger.debug("class of GraphicsConfiguration: "+gc.getClass().getName());
        try {
            rootPanel = new JCanvas3D(new GraphicsConfigTemplate3D());
            rootPanel.setPreferredSize(new Dimension(512, 512));
            rootPanel.setSize(new Dimension(512, 512));

            // canvas3d = rootPanel.getOffscreenCanvas3D();
            // canvas3d.setDoubleBufferEnable(true);

            simpleU = new SimpleUniverse(rootPanel.getOffscreenCanvas3D());
            simpleU.getViewingPlatform().setNominalViewingTransform();
            simpleU.getViewer().getView().setMinimumFrameCycleTime(30);

            VirtualUniverse.setJ3DThreadPriority(Thread.currentThread()
                    .getPriority());

            dataManager = new J3DDataManager();
            dataManager.setParent(this);
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getRootBranch()
     */
    public BranchGroup getRootBranch() {
        return this.rootBranch;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getPluginParser()
     */
    public PluginParser getPluginParser() {
        if (pluginParser == null) {
            pluginParser = new PluginParser(this, getPluginPath());
        }
        return this.pluginParser;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#isHeavyWeight()
     */
    public boolean isHeavyWeight() {
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getUniverse()
     */
    public SimpleUniverse getUniverse() {
        return this.simpleU;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#destroy()
     */
    public void destroy() {
        if (associatedGUIs != null) {
            for (int i = 0; i < associatedGUIs.size(); i++) {
                Object editor = associatedGUIs.get(i);
                if (editor instanceof JInternalFrame) {
                    javax.swing.JInternalFrame window = (javax.swing.JInternalFrame) associatedGUIs
                            .get(i);
                    if (window != null) {
                        window.dispose();
                    }
                } else if (editor instanceof JFrame) {
                    javax.swing.JFrame window = (javax.swing.JFrame) associatedGUIs
                            .get(i);
                    if (window != null) {
                        window.dispose();
                    }
                }
            }
        }
        try {
            removeFromDesktop();
            removeAll();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /**
     * 色々な処理をすることによって, なるべくメモリーを解放する努力をするメソッド
     */
    protected void removeFromDesktop() {
        if (rootBranch != null) {
            rootBranch.detach();
        }

        ((ChaseTransformGroup) getRootTransform()).cleanUp();

        rootBranch = null;
        if (canvas3d != null) {
            try {
                canvas3d.stopRenderer();
            } catch (Exception ex) {
            }
        }

        canvas3d = null;
        if (simpleU != null) {
            simpleU.removeAllLocales();
            simpleU.getViewer().setViewingPlatform(null);
            simpleU.getViewer().getView().removeCanvas3D(canvas3d);
            simpleU.cleanup();
        }
        simpleU = null;
        postCleanUp();
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#postCleanUp()
     */
    public void postCleanUp() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getDisposeOnExit()
     */
    public Vector getDisposeOnExit() {
        return this.associatedGUIs;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.J3DPanel#addDisposeOnExit(java.awt.Window)
     */
    public void addDisposeOnExit(Window window) {
        associatedGUIs.addElement(window);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.acviewer.J3DPanel#addDisposeOnExit(ciss.phase_viewer
     * .mainpanel.InternalFrameChase)
     */
    public void addDisposeOnExit(InternalFrameChase window) {
        associatedGUIs.addElement(window);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#export(java.lang.String)
     */
    public void export(String fileName) throws IOException {
        doExport(fileName);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#export()
     */
    public void export() throws IOException {
        doExport("");
    }

    /**
     * エクスポートする際に利用される, デフォルトの接頭語を返す. デフォルトは"atomic_config"
     * 
     * @return エクスポートの際に利用されるファイル名の接頭部
     */
    protected String getExportFileNamePrefix() {
        return "atomic_config";
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getParentDirectory()
     */
    public String getParentDirectory() {
        return this.parentDir;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#doExportVRML(java.lang.String)
     */
    public void doExportVRML(String fileName) throws IOException {
        VRMLExporter exp = new VRMLExporter(this);
        exp.doExport(fileName);
    }

    private void doExport(String fileName) throws IOException {
        BufferedImage image = this.createImage();
        if (fileName.trim().length() == 0) {
            int width = canvas3d.getSize().width;
            int height = canvas3d.getSize().height;
            ImageIcon icon = new ImageIcon(image);
            exportLabel = new JLabel(icon);
            exportLabel.setSize(width, height);

            PDFExportFileType pdfexport = new PDFExportFileType(this);
            SVGExportFileType svgexport = new SVGExportFileType(this);
            VRMLExportFileType vrmlexport = new VRMLExportFileType(this);

            // ---freeHEP---
            ExportDialog exportD = new ExportDialog();
            Properties prop = new Properties();
            prop.setProperty("org.freehep.util.export.ExportDialog.SaveAsFile",
                    parentDir + File.separator + getExportFileNamePrefix());
            exportD.setUserProperties(prop);
            exportD.addExportFileType(pdfexport);
            exportD.addExportFileType(svgexport);
            exportD.addExportFileType(vrmlexport);
            exportD.showExportDialog((Component) this, "export view as ...",
                    exportLabel, "atomic_config");
            // ---freeHEP---
        } else {
            ImageIO.write((RenderedImage) image, "jpg", new File(fileName));
            logger.info("exported image to file: " + fileName);
        }
        image = null;
    }

    /**
     * エクスポートの際に呼ばれる, こともある
     */
    public void draw(Graphics2D g2d, Rectangle2D rec) {
        exportLabel.print(g2d);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getCanvas()
     */
    public Canvas3D getCanvas() {
        return this.canvas3d;
    }

    private BufferedImage createImage() {
        int width = canvas3d.getSize().width;
        int height = canvas3d.getSize().height;

        BufferedImage bi = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        ImageComponent2D ic2d = new ImageComponent2D(ImageComponent.FORMAT_RGB,
                bi);
        DepthComponentFloat dcf = new DepthComponentFloat(width, height);
        javax.media.j3d.Raster readRaster = new javax.media.j3d.Raster(
                new Point3f(0.0f, 0.0f, 0.0f),
                javax.media.j3d.Raster.RASTER_COLOR, 0, 0, width, height, ic2d,
                dcf);

        GraphicsContext3D gc3d = canvas3d.getGraphicsContext3D();
        gc3d.flush(true);
        gc3d.readRaster(readRaster);

        ImageComponent2D ic = readRaster.getImage();
        return ic.getImage();
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getJ3DDataManager()
     */
    public J3DDataManager getJ3DDataManager() {
        return this.dataManager;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#doRedraw()
     */
    public boolean doRedraw() {
        return doRedraw;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#doRedraw(boolean)
     */
    public void doRedraw(boolean doRedraw) {
        this.doRedraw = doRedraw;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#prepareRedraw()
     */
    public void prepareRedraw() {
        try {
            canvas3d.getView().stopView();
        } catch (Exception exc) {
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#setProjectionPolicy(int)
     */
    public void setProjectionPolicy(int projectionPolicy) {
        canvas3d.getView().setProjectionPolicy(projectionPolicy);
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getProjectionPolicy()
     */
    public int getProjectionPolicy() {
        return canvas3d.getView().getProjectionPolicy();
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#postRedraw()
     */
    public void postRedraw() {
        try {
            canvas3d.getView().startView();
        } catch (Exception exc) {
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getJ3DProperties()
     */
    public GlobalProperties getJ3DProperties() {
        return prop;
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getAssociatedVolumetricData()
     */
    public abstract VolumetricData[] getAssociatedVolumetricData();

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getRootTransform()
     */
    public abstract TransformGroup getRootTransform();

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#getRotatingTransform()
     */
    public TransformGroup[] getRotatingTransform() {
        return new TransformGroup[] { getRootTransform() };
    }

    /*
     * (non-Javadoc)
     * 
     * @see ciss.phase_viewer.acviewer.J3DPanel#supportsFrame()
     */
    public abstract boolean supportsFrame();
}
