/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : MeasureObject.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.awt.Font;
import java.util.Vector;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.LineStripArray;
import javax.media.j3d.OrientedShape3D;
import javax.media.j3d.RestrictedAccessException;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.DefaultListModel;
import javax.vecmath.Color3f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.ConfigDataUpdateEvent;
import ciss.phase_viewer.common.ConstParameters;
import ciss.phase_viewer.common.Utils;

import com.sun.j3d.utils.geometry.Text2D;

public class MeasureObject extends SceneGraphElementBG {
    private static Logger logger = Logger.getLogger(MeasureObject.class
            .getName());

    public final int DISTANCE = 0;
    public final int ANGLE = 1;
    public final int DIHEDRAL = 2;

    private int measureType = DISTANCE;
    private double[][] xyz;
    private double result = 0.d;

    private BranchGroup branchGroup;

    public MeasureObject(double[] atom1, double[] atom2) {
        this.measureType = DISTANCE;
        this.xyz = new double[2][3];
        this.xyz[0] = atom1;
        this.xyz[1] = atom2;
        init();
    }

    public MeasureObject(double[] atom1, double[] atom2, double result) {
        this.measureType = DISTANCE;
        this.xyz = new double[2][3];
        this.xyz[0] = atom1;
        this.xyz[1] = atom2;
        this.result = result;
        init();
    }

    public MeasureObject(double[] atom1, double[] atom2, double[] atom3,
            double result) {
        this.measureType = ANGLE;
        this.xyz = new double[3][3];
        this.xyz[0] = atom1;
        this.xyz[1] = atom2;
        this.xyz[2] = atom3;
        this.result = result;
        init();
    }

    public MeasureObject(double[] atom1, double[] atom2, double[] atom3,
            double[] atom4, double result) {
        this.measureType = DIHEDRAL;
        this.xyz = new double[4][3];
        this.xyz[0] = atom1;
        this.xyz[1] = atom2;
        this.xyz[2] = atom3;
        this.xyz[3] = atom4;
        this.result = result;
        init();
    }

    private Vector atomVector;

    public void setAtomVector(Vector atomVector) {
        this.atomVector = atomVector;
    }

    public Vector getAtomVector() {
        return this.atomVector;
    }

    private void init() {
        setCapability(BranchGroup.ALLOW_DETACH);
        branchGroup = new BranchGroup();
        branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
    }

    private DefaultListModel model;

    public void setAssociatedListModel(DefaultListModel model) {
        this.model = model;
    }

    public void detach() {
        super.detach();
        if (model != null)
            model.removeElement(this);
    }

    public void configDataUpdate() {
    }

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

    public void setResult(double result) {
        this.result = result;
    }

    public void setAtoms(double[][] atoms) {
        this.xyz = atoms;
    }

    private String[] id;

    /**
     * 뫇믦뫮뤭궻뙱럔궸듫귦귡륃뺪궻봹쀱귩긜긞긣
     * 
     * @param idstr
     *            뙱럔궻륃뺪
     */
    public void setIDString(String[] idstr) {
        this.id = idstr;
    }

    protected void create() {
        if (measureType == DISTANCE) {
            createDistanceObject();
        } else if (measureType == ANGLE) {
            createAngleObject();
        } else if (measureType == DIHEDRAL) {
            createDihedObject();
        }
        this.addChild(branchGroup);
    }

    public void recreate() {
        branchGroup.detach();
        branchGroup = new BranchGroup();
        branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
        create();
    }

    public String toString() {
        if (measureType == DISTANCE) {
            return "distance between atoms " + id[1] + " and " + id[0] + ": "
                    + ConstParameters.formater_smaller_digits.format(result)
                    + " 곾";
        } else if (measureType == ANGLE) {
            return "bond angle among atoms " + id[2] + ", " + id[1] + " and "
                    + id[0] + ": "
                    + ConstParameters.formater_smaller_digits.format(result)
                    + " 걢";
        } else if (measureType == DIHEDRAL) {
            return "dihedral angle among atoms " + id[3] + ", " + id[2] + ", "
                    + id[1] + " and " + id[0] + ": "
                    + ConstParameters.formater_smaller_digits.format(result)
                    + " 걢";
        }
        return "";
    }

    private void createDistanceObject() {
        double[] midpoint = { (xyz[0][0] + xyz[1][0]) * 0.5d - 0.1d,
                (xyz[0][1] + xyz[1][1]) * 0.5d, (xyz[0][2] + xyz[1][2]) * 0.5d };
        branchGroup.addChild(new Line(xyz[0], xyz[1]));
        branchGroup.addChild(new Word(ConstParameters.formater_smaller_digits
                .format(result) + " 곾", midpoint, mACVD.getFont()));
    }

    private void createAngleObject() {
        double[] midpoint = { (xyz[0][0] + xyz[2][0]) * 0.5d - 0.1d,
                (xyz[0][1] + xyz[2][1]) * 0.5, (xyz[0][2] + xyz[2][2]) * 0.5 };
        branchGroup.addChild(new Arc(xyz[0], xyz[2], xyz[1]));
        // this.addChild(new Line(xyz[0],xyz[2]));
        branchGroup.addChild(new Word(ConstParameters.formater_smaller_digits
                .format(result) + " 걢", midpoint, mACVD.getFont()));
    }

    private void createDihedObject() {
        double[] a = { (xyz[0][0] + xyz[1][0] + xyz[2][0]) / 3.d,
                (xyz[0][1] + xyz[1][1] + xyz[2][1]) / 3.d,
                (xyz[0][2] + xyz[1][2] + xyz[2][2]) / 3.d };
        double[] b = { (xyz[1][0] + xyz[2][0] + xyz[3][0]) / 3.d,
                (xyz[1][1] + xyz[2][1] + xyz[3][1]) / 3.d,
                (xyz[1][2] + xyz[2][2] + xyz[3][2]) / 3.d };
        double[] c = { (xyz[1][0] + xyz[2][0]) * 0.5d,
                (xyz[1][1] + xyz[2][1]) * 0.5d, (xyz[1][2] + xyz[2][2]) * 0.5d };
        double[] pos = { (a[0] + b[0]) * 0.5d - 0.1d, (a[1] + b[1]) * 0.5d,
                (a[2] + b[2]) * 0.5d };
        branchGroup.addChild(new Arc(a, b, c));
        branchGroup.addChild(new Word(ConstParameters.formater_smaller_digits
                .format(result) + " 걢", pos, mACVD.getFont()));
    }

    public int getMeasureType() {
        return this.measureType;
    }

    public int getType() {
        return super.MEASURE;
    }

    class Word extends TransformGroup {
        protected Word(String text, double[] pos, Font font) {
            super();
            createWord(text, pos, font);
        }

        private void createWord(String text, double[] pos, Font font) {
            Color3f col = mACVD.getFontColor();
            Text2D text2d = new CapableText2D(text, col, font.getName(),
                    font.getSize(), font.getStyle());
            Transform3D t3d = new Transform3D();
            t3d.set(new Vector3d(pos));
            this.setTransform(t3d);

            OrientedShape3D orientedShape = new OrientedShape3D(
                    text2d.getGeometry(), text2d.getAppearance(),
                    OrientedShape3D.ROTATE_ABOUT_POINT, new Point3f(0.1f, 0.0f,
                            0.0f));
            this.addChild(orientedShape);
        }
    }

    class Line extends Shape3D {
        protected Line(double[] a, double[] b) {
            super();
            createLine(a, b);
        }

        private void createLine(double[] a, double[] b) {
            float resolution = 100;
            float invresolution = 1.f / resolution;
            int length = (int) resolution;

            double[] ems = { (b[0] - a[0]), (b[1] - a[1]), (b[2] - a[2]) };
            double[] midpoint = { (b[0] + a[0]) * 0.5 - 0.1f,
                    (b[1] + a[1]) * 0.5, (b[2] + a[2]) * 0.5 + 0.05f };

            Appearance appearance = new Appearance();
            appearance.setColoringAttributes(new ColoringAttributes(0.9f, 0.9f,
                    0.5f, ColoringAttributes.NICEST));

            LineAttributes lineAttrib = new LineAttributes();
            lineAttrib.setLineWidth(5.0f);
            lineAttrib.setLinePattern(LineAttributes.PATTERN_DASH);
            appearance.setLineAttributes(lineAttrib);

            LineStripArray lsa = new LineStripArray(length,
                    GeometryArray.COORDINATES, new int[] { length });
            for (int i = 0; i < resolution; i++) {
                float[] fp = new float[3];
                for (int j = 0; j < 3; j++) {
                    fp[j] = (float) ems[j] * invresolution * (float) i
                            + (float) a[j] + 0.01f;
                }
                lsa.setCoordinate(i, new Point3f(fp));
            }
            try {
                lsa.setCapability(LineStripArray.ALLOW_INTERSECT);
            } catch (RestrictedAccessException rae) {
            }
            this.setAppearance(appearance);
            this.setGeometry(lsa);
        }

    }

    class Arc extends Shape3D {
        protected Arc(double[] a, double[] b, double[] center) {
            super();
            createArc(a, b, center);
        }

        private void createArc(double[] a, double[] b, double[] center) {
            float resolution = 100;
            float invresolution = 1.f / resolution;
            int length = (int) resolution;

            double[] bma = { b[0] - a[0], b[1] - a[1], b[2] - a[2] };
            double[] amc = { a[0] - center[0], a[1] - center[1],
                    a[2] - center[2] };
            double radius = Utils.getNorm(amc);

            double[] oap = new double[3];
            double[][] point = new double[length][3];
            for (int i = 0; i < length; i++) {
                for (int j = 0; j < 3; j++) {
                    double tmp = bma[j] * invresolution * (float) i + a[j];
                    oap[j] = tmp - center[j];
                }
                Utils.normalize(oap);
                for (int j = 0; j < 3; j++) {
                    point[i][j] = oap[j] * radius + center[j];
                }
            }

            Appearance appearance = new Appearance();
            appearance.setColoringAttributes(new ColoringAttributes(0.9f, 0.9f,
                    0.5f, ColoringAttributes.NICEST));

            LineAttributes lineAttrib = new LineAttributes();
            lineAttrib.setLineWidth(5.0f);
            lineAttrib.setLinePattern(LineAttributes.PATTERN_DASH);
            appearance.setLineAttributes(lineAttrib);

            LineStripArray lsa = new LineStripArray(length,
                    GeometryArray.COORDINATES, new int[] { length });
            Point3f pt = new Point3f();
            for (int i = 0; i < length; i++) {
                pt.x = (float) point[i][0];
                pt.y = (float) point[i][1];
                pt.z = (float) point[i][2];
                lsa.setCoordinate(i, pt);
            }
            try {
                lsa.setCapability(LineStripArray.ALLOW_INTERSECT);
            } catch (RestrictedAccessException rae) {
            }
            this.setAppearance(appearance);
            this.setGeometry(lsa);
        }
    }

}
