/*
 * Copyright (C) 2013-2015 F(X)yz, 
 * Sean Phillips, Jason Pollastrini and Jose Pereda
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fxyz.shapes.composites;

import java.util.ArrayList;
import javafx.scene.AmbientLight;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import org.fxyz.geometry.Point3D;

/**
 *
 * @author Sean
 */
public class ScatterPlotMesh extends Group {
       private ArrayList<Double> xAxisData = new ArrayList<>();
    private ArrayList<Double> yAxisData = new ArrayList<>();
    private ArrayList<Double> zAxisData = new ArrayList<>(); 
    public Group scatterDataGroup = new Group();    
    public AmbientLight selfLight = new AmbientLight(Color.WHITE);
    public double nodeRadius = 1;    
    private double axesSize = 1000;
    private boolean normalized = false;
    public boolean selfLightEnabled = true;
    
        double plotSize = 1000;
        double nodeSize = 1;
    public enum NodeType {SPHERE, CUBE, PYRAMID, STAR};
    private NodeType defaultNodeType = NodeType.SPHERE;
    
    public ScatterPlotMesh(boolean selfLit) {
        selfLightEnabled = selfLit;
        init();
    }
    public ScatterPlotMesh(double axesSize, double nodeRadius, boolean selfLit) {
        selfLightEnabled = selfLit;
        this.axesSize = axesSize;
        this.nodeRadius = nodeRadius;
        init();
    }    
    private void init(){
        if(selfLightEnabled) {
            getChildren().add(selfLight);
        }
        setDepthTest(DepthTest.ENABLE);        
    }     
    public void setXYZData(ArrayList<Double> xData, ArrayList<Double> yData, ArrayList<Double> zData) {
        xAxisData = xData;
        yAxisData = yData;
        zAxisData = zData;
        getChildren().clear();
        //for now we will always default to x axis
        //later we could maybe dynamically determine the smallest axis and then
        //uses 0's for the other axes that are larger.
        ArrayList<Point3D> point3DList = new ArrayList<>();

        for(int i=0;i<xAxisData.size();i++) {
            //some safety checks for array sizes
            double translateY = 0.0;
            double translateZ = 0.0;            
            if(!yAxisData.isEmpty() && yAxisData.size() > i)
                translateY = yAxisData.get(i);
            if(!zAxisData.isEmpty() && zAxisData.size() > i)
                translateZ = zAxisData.get(i);
            setTranslateX(xAxisData.get(i));
            //Convert to Floats and build list of adjusted points
            point3DList.add(new Point3D(new Float(xAxisData.get(i)), new Float(translateY), new Float(translateZ)));

            float width = 1;
            final TriangleMesh mesh = new TriangleMesh();
            //add each point. For each point add another point shifted on Z axis by width
            //This extra point allows us to build triangles later
            for(Point3D point: point3DList) {
                //Rear points
                //top right rear point
                mesh.getPoints().addAll(point.x+width,point.y+width,point.z+width);
                //top left rear point
                mesh.getPoints().addAll(point.x-width,point.y+width,point.z+width);
                //bottom right rear point
                mesh.getPoints().addAll(point.x+width,point.y-width,point.z+width);
                //bottom left rear point
                mesh.getPoints().addAll(point.x-width,point.y-width,point.z+width);
                //Front points
                //top right front point
                mesh.getPoints().addAll(point.x+width,point.y+width,point.z-width);
                //top left front point
                mesh.getPoints().addAll(point.x-width,point.y+width,point.z-width);
                //bottom right front point
                mesh.getPoints().addAll(point.x+width,point.y-width,point.z-width);
                //bottom left front point
                mesh.getPoints().addAll(point.x-width,point.y-width,point.z-width);
            }
            //add dummy Texture Coordinate
            mesh.getTexCoords().addAll(0,0);
            //Now generate nodes for each point
            for(int p=8;p<point3DList.size()*7;p+=8) {  //add each segment
                //Wind the next 8 vertices as a cube.  The cube itself will represent the data
                //Vertices wound counter-clockwise which is the default front face of any Triangle
                //Rear triangle faces should be wound clockwise to face away from center
                mesh.getFaces().addAll(p,0,p+3,0,p+2,0); //TRR,BLR,BRR
                mesh.getFaces().addAll(p+3,0,p,0,p+1,0); //BLR,TRR,TLR
                //left side faces
                mesh.getFaces().addAll(p+1,0,p+5,0,p+3,0); //TLR,TLF,BLR
                mesh.getFaces().addAll(p+5,0,p+7,0,p+3,0); //TLF,BLR,BLF
                //front side faces
                mesh.getFaces().addAll(p+5,0,p+7,0,p+4,0); //TLF,BLF,TLR
                mesh.getFaces().addAll(p+4,0,p+7,0,p+6,0); //TRF,BLF,BRF
                //front side faces
                mesh.getFaces().addAll(p+4,0,p+6,0,p+2,0); //TRF,BRF,BRR
                mesh.getFaces().addAll(p+4,0,p+2,0,p,0); //TRF,BRR,TRR
               
                //Top faces
                mesh.getFaces().addAll(p,0,p+1,0,p+3,0); //TRR,TLR,TRF
                mesh.getFaces().addAll(p+1,0,p+5,0,p+3,0); //TLR,TLF,TRF
               
                //bottom faces
                mesh.getFaces().addAll(p+3,0,p+7,0,p+6,0); //BLR,BLF,BRF
                mesh.getFaces().addAll(p+3,0,p+6,0,p+2,0); //BLR,BRF,BRR
            }
           
            //Need to add the mesh to a MeshView before adding to our 3D scene
            MeshView meshView = new MeshView(mesh);
            meshView.setDrawMode(DrawMode.FILL);  //Fill so that the line shows width
                              
            Color hsb = Color.hsb((new Double(i)  / 12) * 360, 1.0, 1.0, 0.5);
            PhongMaterial material = new PhongMaterial(hsb);
            material.setDiffuseColor(hsb);
            material.setSpecularColor(hsb);
            meshView.setMaterial(material);
            //Make sure you Cull the Back so that no black shows through
            meshView.setCullFace(CullFace.BACK);    
//            //Add some ambient light so folks can see it
//            Group line = new Group();
//            AmbientLight light = new AmbientLight(Color.WHITE);
//            light.getScope().add(meshView);
//            line.getChildren().add(light);
//            line.getChildren().add(meshView);           
            getChildren().addAll(meshView);           
        }
    }
    
}