/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : MaterialEditor.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.
!
!=======================================================================
 */
// Copyright (C) 2005 Jun Mitani
package ciss.phase_viewer.pub.materialeditor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Light;
import javax.media.j3d.Material;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TransparencyAttributes;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextArea;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;

public class MaterialEditor extends JFrame implements ActionListener,
        ChangeListener, AdjustmentListener {

    // レンダリングキャンバス
    private final static int RENDER_CANVAS_SIZE = 200;
    private final static int RENDER_CANVAS_BG_GRID_NUM = 8;

    // パラメータ識別ID
    private final static int ID_AMBIENT = 0;
    private final static int ID_DIFFUSE = 1;
    private final static int ID_SPECULAR = 2;
    private final static int ID_EMISSION = 3;
    private final static int ID_SHINIESS = 4;
    private final static int ID_TRANSPARENCY = 5;

    // UI部品
    private JLabel paramNameLabels[] = new JLabel[6];
    private JButton paramButtons[] = new JButton[4];
    private JScrollBar paramScrollBars[] = new JScrollBar[2];
    private JColorChooser colorChooser;
    private JButton outputButton;
    private Color paramColors[] = { new Color(87, 61, 54),
            new Color(161, 50, 47), new Color(59, 60, 21),
            new Color(20, 17, 15) };
    private int selectedParamID = 0;
    String labelNames[] = { "Ambient(環境光)", "Diffuse(拡散光)", "Specular(鏡面光)",
            "Emission(放射光)", "Shiniess(輝度)", "Transparency(透明度)" };

    private Appearance m_appearance;

    // レンダリングキャンパスの作成
    private Canvas3D createCanvas() {
        GraphicsConfiguration config = SimpleUniverse
                .getPreferredConfiguration();
        Canvas3D canvas = new Canvas3D(config);
        canvas.setSize(RENDER_CANVAS_SIZE, RENDER_CANVAS_SIZE);

        SimpleUniverse universe = new SimpleUniverse(canvas);
        universe.getViewingPlatform().setNominalViewingTransform();
        universe.addBranchGraph(createBranchGraph());
        return canvas;
    }

    // BranchGroup の作成
    private BranchGroup createBranchGraph() {
        BranchGroup branchGroup = new BranchGroup();

        // 平行光源の追加
        branchGroup.addChild(createDirectionalLight());

        // 環境光の追加
        branchGroup.addChild(createAmbientLight());

        // 背景イメージの追加
        branchGroup.addChild(createBackgroundImage());

        // レンダリング対象の球体を追加
        branchGroup.addChild(createSphere());

        branchGroup.compile();

        return branchGroup;
    }

    // 平行光源の作成
    private Light createDirectionalLight() {
        DirectionalLight light = new DirectionalLight(true, new Color3f(1.0f,
                1.0f, 1.0f), new Vector3f(-1.0f, -1.0f, -1.0f));
        light.setInfluencingBounds(new BoundingSphere(new Point3d(), 10.0));
        return light;
    }

    // 環境光の作成
    private Light createAmbientLight() {
        AmbientLight light = new AmbientLight();
        light.setInfluencingBounds(new BoundingSphere(new Point3d(), 10.0));
        return light;
    }

    // 背景画像の作成
    private Background createBackgroundImage() {
        BufferedImage bufferedImage = new BufferedImage(RENDER_CANVAS_SIZE,
                RENDER_CANVAS_SIZE, BufferedImage.TYPE_3BYTE_BGR);
        Graphics2D g = bufferedImage.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, RENDER_CANVAS_SIZE, RENDER_CANVAS_SIZE);
        g.setColor(Color.black);
        int gridSize = RENDER_CANVAS_SIZE / RENDER_CANVAS_BG_GRID_NUM;
        for (int y = 0; y < RENDER_CANVAS_BG_GRID_NUM; y++) {
            for (int x = 0; x < RENDER_CANVAS_BG_GRID_NUM; x++) {
                if ((x + y) % 2 == 0) {
                    g.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
                }
            }
        }
        Background background = new Background(new ImageComponent2D(
                ImageComponent2D.FORMAT_RGB, bufferedImage));
        background
                .setApplicationBounds(new BoundingSphere(new Point3d(), 10.0));
        return background;
    }

    // Sphere の作成
    private Sphere createSphere() {
        // Material の作成
        Material material = new Material();
        material.setCapability(Material.ALLOW_COMPONENT_READ);
        material.setCapability(Material.ALLOW_COMPONENT_WRITE);
        material.setLightingEnable(true);

        // TransparencyAttributes の作成
        TransparencyAttributes tansparency = new TransparencyAttributes(
                TransparencyAttributes.BLENDED, 0.0f);
        tansparency.setCapability(TransparencyAttributes.ALLOW_VALUE_READ);
        tansparency.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);

        // Appearance の作成
        m_appearance = new Appearance();
        m_appearance.setCapability(Appearance.ALLOW_MATERIAL_READ);
        m_appearance.setCapability(Appearance.ALLOW_MATERIAL_WRITE);
        m_appearance
                .setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_READ);
        m_appearance
                .setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);

        // Appearance に Material を設定
        m_appearance.setMaterial(material);

        // Appearance に TransparencyAttributes を設定
        m_appearance.setTransparencyAttributes(tansparency);

        // Sphere の作成
        Sphere sphere = new Sphere(0.7f, Sphere.GENERATE_NORMALS, 128,
                m_appearance);
        sphere.getShape().setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
        sphere.getShape().setCapability(Shape3D.ALLOW_APPEARANCE_READ);

        return sphere;
    }

    // パラメータの選択
    private void setSelectedParamID(int id) {
        paramNameLabels[selectedParamID].setBorder(null);
        selectedParamID = id;
        paramNameLabels[selectedParamID].setBorder(BorderFactory
                .createLineBorder(Color.blue));

        colorChooser.setBorder(BorderFactory
                .createTitledBorder(labelNames[selectedParamID]));
        colorChooser.setColor(paramColors[selectedParamID]);
    }

    // パラメータ編集パネルの作成
    private JPanel createParamEditPanel() {
        JPanel panel = new JPanel();
        panel.setPreferredSize(new Dimension(300, RENDER_CANVAS_SIZE));
        panel.setLayout(new GridLayout(6, 2, 10, 5));

        for (int i = 0; i < 6; i++) {
            // ラベルの追加
            panel.add(paramNameLabels[i] = new JLabel(labelNames[i],
                    JLabel.RIGHT));

            if (i < 4) {
                // ボタンの追加
                paramButtons[i] = new JButton();
                panel.add(paramButtons[i]);
                paramButtons[i].addActionListener(this);
            } else {
                // スクロールバーの追加
                int barIndex = i - 4;
                paramScrollBars[barIndex] = new JScrollBar(
                        JScrollBar.HORIZONTAL, 0, 1, 0, 100);
                paramScrollBars[barIndex].addAdjustmentListener(this);
                panel.add(paramScrollBars[barIndex]);
            }
        }

        return panel;
    }

    // GUI部品の構築
    private void buildUI() {
        // 上部パネル（レンダリングキャンパス＋パラメータ編集パネル）の作成
        JPanel panelNorth = new JPanel();
        panelNorth.setLayout(new FlowLayout());
        panelNorth.add(createCanvas());
        panelNorth.add(createParamEditPanel());

        // カラー選択パネルの作成
        JPanel panelCenter = new JPanel();
        colorChooser = new JColorChooser();
        colorChooser.getSelectionModel().addChangeListener(this);
        panelCenter.add(colorChooser);

        // 下部パネル（コード出力ボタン）の作成
        JPanel panelSouth = new JPanel();
        panelSouth.add(outputButton = new JButton("コード出力"));
        outputButton.addActionListener(this);

        // 上部パネルと下部パネルをフレームに追加
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(panelNorth, BorderLayout.NORTH);
        getContentPane().add(panelCenter, BorderLayout.CENTER);
        getContentPane().add(panelSouth, BorderLayout.SOUTH);
    }

    // 新しい色が選択された時の処理
    public void stateChanged(ChangeEvent e) {
        setMaterialParam(selectedParamID, colorChooser.getColor());
    }

    // 環境光、拡散光、鏡面光、放射光のパラメータを設定
    private void setMaterialParam(int paramID, Color c) {
        paramColors[paramID] = c;
        paramButtons[paramID].setBackground(c);

        Material material = m_appearance.getMaterial();
        switch (paramID) {
        case ID_AMBIENT:
            material.setAmbientColor(new Color3f(c));
            break;
        case ID_DIFFUSE:
            material.setDiffuseColor(new Color3f(c));
            break;
        case ID_SPECULAR:
            material.setSpecularColor(new Color3f(c));
            break;
        case ID_EMISSION:
            material.setEmissiveColor(new Color3f(c));
            break;
        }
    }

    // 輝度、透明度のパラメータを設定
    private void setMaterialParam(int paramID, float value) {
        switch (paramID) {
        case ID_SHINIESS:
            m_appearance.getMaterial().setShininess(value);
            break;
        case ID_TRANSPARENCY:
            m_appearance.getTransparencyAttributes().setTransparency(value);
            break;
        }
    }

    // スクロールバーが操作された時の処理
    public void adjustmentValueChanged(AdjustmentEvent e) {
        if (e.getAdjustable().equals(paramScrollBars[0])) {
            setMaterialParam(ID_SHINIESS, paramScrollBars[0].getValue() * 1.28f);
        } else {
            setMaterialParam(ID_TRANSPARENCY,
                    paramScrollBars[1].getValue() / 100.0f);
        }
    }

    // ボタンが押された時の処理
    public void actionPerformed(ActionEvent e) {
        if (e.getSource().equals(outputButton)) {
            outputCode();
        } else {
            for (int i = 0; i < 4; i++) {
                if (e.getSource().equals(paramButtons[i])) {
                    setSelectedParamID(i);
                    break;
                }
            }
        }
    }

    // Appearance 設定用コードの出力
    private void outputCode() {
        Material material = m_appearance.getMaterial();
        Color3f color = new Color3f();

        JFrame frame = new JFrame("Code");
        frame.setBounds(100, 100, 500, 250);
        JTextArea textArea = new JTextArea();
        textArea.setLineWrap(true);
        DecimalFormat form = new DecimalFormat("0.0000");

        textArea.append("Material material = new Material();\n");

        material.getAmbientColor(color);
        textArea.append("material.setAmbientColor(" + form.format(color.x)
                + "f, " + form.format(color.y) + "f, " + form.format(color.z)
                + "f);\n");

        material.getDiffuseColor(color);
        textArea.append("material.setDiffuseColor(" + form.format(color.x)
                + "f, " + form.format(color.y) + "f, " + form.format(color.z)
                + "f);\n");

        material.getSpecularColor(color);
        textArea.append("material.setSpecularColor(" + form.format(color.x)
                + "f, " + form.format(color.y) + "f, " + form.format(color.z)
                + "f);\n");

        material.getEmissiveColor(color);
        textArea.append("material.setEmissiveColor(" + form.format(color.x)
                + "f, " + form.format(color.y) + "f, " + form.format(color.z)
                + "f);\n");

        float shininess = material.getShininess();
        textArea.append("material.setShininess(" + form.format(shininess)
                + "f);\n");

        textArea.append("TransparencyAttributes tansparency = new TransparencyAttributes(TransparencyAttributes.BLENDED, "
                + form.format(m_appearance.getTransparencyAttributes()
                        .getTransparency()) + "f);\n");

        textArea.append("Appearance appearance = new Appearance();\n");
        textArea.append("appearance.setMaterial(material);\n");
        textArea.append("appearance.setTransparencyAttributes(tansparency);\n");

        frame.add(textArea);
        frame.setVisible(true);
    }

    // MaterialEditor のコンストラクタ
    public MaterialEditor() {
        super("MaterialEditor");
        // UI部品の構築
        buildUI();

        // パラメータの初期化
        setSelectedParamID(0);
        for (int i = 0; i < 4; i++) {
            setMaterialParam(i, paramColors[i]);
        }

        paramScrollBars[0].setValue(30);
        paramScrollBars[1].setValue(3);
        setMaterialParam(ID_SHINIESS, 128.0f * 0.3f);
        setMaterialParam(ID_TRANSPARENCY, 0.03f);
    }

    public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);
        MaterialEditor materialEditor = new MaterialEditor();
        materialEditor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        materialEditor.setBounds(10, 10, 600, 650);
        materialEditor.setVisible(true);
    }
}
