package endu;

import vtk.util.VtkPanelContainer;
import vtk.util.VtkPanelUtil;
import vtk.util.VtkUtil;
import vtk.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.StreamTokenizer;

/**
 * This example demonstrates the use of fields and use of
 * vtkProgrammableDataObjectSource. It creates fields the hard way (as compared
 * to reading a vtk field file), but shows you how to interface to your own raw
 * data.
 */
public class EnduModel extends JComponent implements VtkPanelContainer {

  public static	vtkCamera camera;
  public static	double cameraX = 0.5;
  public static	double cameraY = -1;
  public static	double cameraZ = -2.5;
  public static	double focalX = 0.5;
  public static	double focalY = 0.5;
  public static	double focalZ = 0.5;
  
  private final static String NUMBER_POINTS = "NUMBER_POINTS";
  private final static String X_AXIS = "X";
  private final static String Y_AXIS = "Y";
  private final static String Z_AXIS = "Depth (m)";
  private final static String scalar = "SCA";

  public static vtkPanel renWin;
  public static vtkRenderer ren;
  public static vtkActor[][] actorArray;
  
  public static	double zoomStep = 0.2;
  public static double scaleXY = 10;
  public static double scaleDepth = 50;
  public static double radius =0.02;    
  public static int dataReduction1 = 10;      
  public static int dataReduction2 = 10;      
  public static double missingValue = -999.;  
  public static String missingDate = "0/0/0";  
   
  public static double radius2 =0.012;  
  public static double colorRate = 1;
  public static int[] colorMeasure = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
  public static int[] slotMeasure = {-1,-1,-1,-1,-1,-1,-1,-1};
  public static int[] measureSlot = {-1,-1,-1,-1,-1,-1,-1,-1};
        
  
  private vtkProgrammableDataObjectSource dos;
  private vtkGaussianSplatter popSplatter;
  private vtkFollower xActor;
  private vtkFollower yActor;
  private vtkFollower zActor;
  private vtkActor axesActor;
  public static vtkActor planeSphereActor1,planeSphereActor2, planeSphereActor3;  
  
  public static vtkFollower[] xTextActor = new vtkFollower[1000];
  public static vtkFollower[] yTextActor = new vtkFollower[1000];
  public static vtkFollower[] zTextActor = new vtkFollower[1000];
  public static vtkActor planeActor;
  public static vtkActor planeActor2;
  public static vtkTextActor[][] textActor = new vtkTextActor[8][4];
  public static vtk.vtkActor2D[][] legendActor = new vtk.vtkActor2D[8][3];
  
  public EnduModel() {
    // Setup VTK rendering panel
    renWin = new vtkPanel();


    // Parse an ASCII file and manually create a field. Then construct a
    // dataset from the field.
    dos = new vtkProgrammableDataObjectSource();

    // Arrange to call the parsing function when the programmable data
    // source is executed.
    dos.SetExecuteMethod(this, "parseFile");

    // Create the dataset.

    // DataObjectToDataSetFilter can create geometry using fields from
    // DataObject's FieldData
    vtkDataObjectToDataSetFilter do2ds = new vtkDataObjectToDataSetFilter();
    do2ds.SetInput(dos.GetOutput());
    // We are generating polygonal data
    do2ds.SetDataSetTypeToPolyData();
    do2ds.DefaultNormalizeOn();
    // All we need is points. Assign them.
    do2ds.SetPointComponent(0, X_AXIS, 0);
    do2ds.SetPointComponent(1, Y_AXIS, 0);
    do2ds.SetPointComponent(2, Z_AXIS, 0);

    // RearrangeFields is used to move fields between DataObject's
    // FieldData, PointData and CellData.
    vtkRearrangeFields rf = new vtkRearrangeFields();
    rf.SetInput(do2ds.GetOutput());
    // Add an operation to "move TIME_LATE from DataObject's FieldData to
    // PointData"
    rf.AddOperation("MOVE", scalar, "DATA_OBJECT", "POINT_DATA");
    // Force the filter to execute. This is need to force the pipeline
    // to execute so that we can find the range of the array TIME_LATE
    rf.Update();
    // Set max to the second (GetRange returns [min,max]); of the "range of the
    // array called scalar in the PointData of the output of rf"
    double maxRange = rf.GetOutput().GetPointData().GetArray(scalar).GetRange()[1];


    // Use an ArrayCalculator to normalize TIME_LATE
    vtkArrayCalculator calc = new vtkArrayCalculator();
    calc.SetInput(rf.GetOutput());
    // Working on point data
    calc.SetAttributeModeToUsePointData();
    // Map scalar to s. When setting function, we can use s to
    // represent the array scalar (TIME_LATE);
    calc.AddScalarVariable("s", scalar, 0);
    // Divide scalar by max (applies division to all components of the array);
    calc.SetFunction("s / " + maxRange);
    // The output array will be called resArray
    calc.SetResultArrayName("resArray");

    // Use AssignAttribute to make resArray the active scalar field
    vtkAssignAttribute aa = new vtkAssignAttribute();
	aa.SetInput(calc.GetOutput());
	aa.Assign("resArray", "SCALARS", "POINT_DATA");
	aa.Update();
    // construct pipeline for original population
    // GaussianSplatter -> Contour -> Mapper -> Actor
    popSplatter = new vtkGaussianSplatter();
    popSplatter.SetInput(aa.GetOutput());
    popSplatter.SetSampleDimensions(50, 50, 50);
    popSplatter.SetRadius(0.02);
    popSplatter.ScalarWarpingOff();

    vtkContourFilter popSurface = new vtkContourFilter();
    popSurface.SetInput(popSplatter.GetOutput());
    popSurface.SetValue(0, 0.01);

    vtkPolyDataMapper popMapper = new vtkPolyDataMapper();
    popMapper.SetInput(popSurface.GetOutput());
    popMapper.ScalarVisibilityOff();

 
    ren = renWin.GetRenderer();
    camera = new vtkCamera();
    createAxes();
    createTextXY();
    createTextDepth();
    createImage();
    util.createLegendMissingValue();        
    
    
    
    
    ren.AddActor(axesActor);
    ren.AddActor(xActor);
    ren.AddActor(yActor);
    ren.AddActor(zActor);
    
    
    vtkSphereSource s = new vtkSphereSource();
	s.SetRadius(radius2);
	s.SetCenter(0,0,0);  
	vtkPolyDataMapper sphereMapper = new vtkPolyDataMapper();
	sphereMapper.SetInputConnection( s.GetOutputPort() );
	vtkActor sphereActor0 = new vtkActor();
	sphereActor0.SetMapper(sphereMapper );
	sphereActor0.GetProperty().SetColor(0, 0, 0);
	ren.AddActor( sphereActor0 );
	
	for (int i=0; i<=Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY));i++){
		vtkSphereSource s2 = new vtkSphereSource();
		s2.SetRadius(radius2);
	    s2.SetCenter((double) i/(double) scaleXY,0,0);  
	    vtkPolyDataMapper sphereMapper2 = new vtkPolyDataMapper();
	    sphereMapper2.SetInputConnection( s2.GetOutputPort() );
	    vtkActor sphereActor2 = new vtkActor();
	    sphereActor2.SetMapper(sphereMapper2 );
	    sphereActor2.GetProperty().SetColor(0, 0, 0);
	    ren.AddActor( sphereActor2 );
	}
	for (int i=0; i<=Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY));i++){
		vtkSphereSource s2 = new vtkSphereSource();
		s2.SetRadius(radius2);
	    s2.SetCenter(0,(double) i/(double) scaleXY,0);  
	    vtkPolyDataMapper sphereMapper2 = new vtkPolyDataMapper();
	    sphereMapper2.SetInputConnection( s2.GetOutputPort() );
	    vtkActor sphereActor2 = new vtkActor();
	    sphereActor2.SetMapper(sphereMapper2 );
	    sphereActor2.GetProperty().SetColor(0, 0, 0);
	    ren.AddActor( sphereActor2 );
	}
	for (int i=0; i<=Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY));i++){
		vtkSphereSource s2 = new vtkSphereSource();
		s2.SetRadius(radius2);
	    s2.SetCenter(0,0,(double) i/(double) scaleXY);  
	    vtkPolyDataMapper sphereMapper2 = new vtkPolyDataMapper();
	    sphereMapper2.SetInputConnection( s2.GetOutputPort() );
	    vtkActor sphereActor2 = new vtkActor();
	    sphereActor2.SetMapper(sphereMapper2 );
	    sphereActor2.GetProperty().SetColor(0, 0, 0);
	    ren.AddActor( sphereActor2 );
	}
    	
    ren.SetBackground(1, 1, 0.8);

    // Set the default camera position
    camera.SetClippingRange(.274, 13.72);
    camera.SetFocalPoint(focalX, focalY, focalZ);
    camera.SetPosition(cameraX,cameraY,cameraZ);
    camera.SetViewUp(10, 0 , 0);
    ren.SetActiveCamera(camera);
    // Assign the camera to the followers.
    xActor.SetCamera(camera);
    yActor.SetCamera(camera);
    zActor.SetCamera(camera);
    
    // Place renWin in the center of this panel
    setLayout(new BorderLayout());
    add(renWin, BorderLayout.CENTER);
// VtkPanelUtil.setSize(renWin, 1160, 810);
  }

  public vtkPanel getRenWin() {
    return renWin;
  }

  /**
	 * Gets next token from stream tokenizer, assume that is a number. Ignores
	 * 'end of line's.
	 * 
	 * @param st
	 *            stream tokenizer to get next number from.
	 * @return value of next number token.
	 * @throws IOException
	 *             If a token is not a number or not 'end of line'.
	 */
  private static double readNumber(StreamTokenizer st) throws IOException {
    int tokenType = st.nextToken();

    // Skip line ends
    while (tokenType == StreamTokenizer.TT_EOL)
      tokenType = st.nextToken();

    if (tokenType == StreamTokenizer.TT_NUMBER) {
      return st.nval;
    }

    // Throw exception since current token is not a number.
    if (tokenType == StreamTokenizer.TT_EOF) {
      throw new IOException("Unexpected end of file.");
    } else if (tokenType == StreamTokenizer.TT_WORD) {
      throw new IOException("Line " + st.lineno()
          + ". Looking for number got word '" + st.sval + "'.");
    } else {
      throw new IOException("Line " + st.lineno()
          + ". Looking for number unknow token type " + tokenType + ".");
    }
  }

  /**
	 * Read data from a file.
	 */
  public void parseFile() throws Exception {

    String fileName = VtkUtil.getVtkDataRoot() + "/Data/endurance.txt";
    BufferedReader reader = new BufferedReader(new FileReader(fileName));

    // Setup stream tokenizer
    StreamTokenizer st = new StreamTokenizer(reader);
    st.eolIsSignificant(true);
    st.wordChars(33, 126);
    st.parseNumbers();

    // Expecting that first token in the file is 'NUMBER_POINTS'
    st.nextToken();
    if (!NUMBER_POINTS.equals(st.sval)) {
      throw new Exception("Expeting a keword " + NUMBER_POINTS + ", got '" + st.sval + "'.");
    }

    // Second token should be the actual number of points
    if (st.nextToken() != StreamTokenizer.TT_NUMBER) {
      throw new Exception("Expeting a number of points, got '" + st.sval + "'.");
    }

    int numPoints = (int) st.nval;

    // Get the data object's field data and allocate
    // room for 4, fields
    vtkFieldData fieldData = dos.GetOutput().GetFieldData();
    fieldData.AllocateArrays(4);

    // Read arrays
    fieldData.AddArray(readArray(st, "SCA", numPoints));
// fieldData.AddArray(readArray(st, "X", numPoints));
// fieldData.AddArray(readArray(st, "Y", numPoints));
// fieldData.AddArray(readArray(st, "Dep", numPoints));
    	
    	ReadCSV.readEndurance();
    	fieldData.AddArray(ReadCSV.arrayX);
    	fieldData.AddArray(ReadCSV.arrayY);
    	fieldData.AddArray(ReadCSV.arrayDepth);
    	   
  }

  /**
	 * Search for an array with given name and read its poins.
	 * 
	 * @param st
	 *            stream tokenizer to get next number from.
	 * @param arrayName
	 *            name of the array to read.
	 * @param numPoints
	 *            expected number of points in the array.
	 * @return named array of points.
	 */
  private static vtkFloatArray readArray(StreamTokenizer st, String arrayName, int numPoints)
      throws Exception {
    // read TIME_LATE - dependent variable
    // search the file until an array called TIME_LATE is found
    while (!arrayName.equals(st.sval)) {
      if (st.nextToken() == StreamTokenizer.TT_EOF)
        throw new Exception("Unexpected end of file looking for '" + arrayName + "'.");
    }

    // Create the corresponding float array
    vtkFloatArray array = new vtkFloatArray();
    array.SetName(arrayName);
    for (int i = 0; i < numPoints; ++i) {
      array.InsertNextValue(readNumber(st));
    }


    return array;
  }


  /**
	 * Create axis, this is for decoration only.
	 */
  private void createAxes() {
    // Create axes.
    popSplatter.Update();
   // double[] bounds = popSplatter.GetOutput().GetBounds();
    vtkAxes axes = new vtkAxes();
    axes.SetOrigin(0,0,0);
   
    axes.SetScaleFactor((double) Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY))/ (double) scaleXY);
    
    vtkTubeFilter axesTubes = new vtkTubeFilter();
    axesTubes.SetInput(axes.GetOutput());
    axesTubes.SetRadius(0.01);
    axesTubes.SetNumberOfSides(10);

    vtkPolyDataMapper axesMapper = new vtkPolyDataMapper();
    axesMapper.SetInput(axesTubes.GetOutput());

    axesActor = new vtkActor();
   // axesActor.GetProperty().SetColor(0, 1, 1);
    axesActor.SetMapper(axesMapper);

    // Label the axes.
    vtkVectorText xText = new vtkVectorText();
    xText.SetText(X_AXIS);

    vtkPolyDataMapper xTextMapper = new vtkPolyDataMapper();
    xTextMapper.SetInput(xText.GetOutput());

    xActor = new vtkFollower();
    xActor.SetMapper(xTextMapper);
    xActor.SetScale(0.04, .04, .04);
    xActor.SetPosition(-0.05, Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY))/scaleXY, -0.05);
    xActor.GetProperty().SetColor(0.6, 0.6, 0);

     
    vtkVectorText yText = new vtkVectorText();
    yText.SetText(Y_AXIS);

    vtkPolyDataMapper yTextMapper = new vtkPolyDataMapper();
    yTextMapper.SetInput(yText.GetOutput());

    yActor = new vtkFollower();
    yActor.SetMapper(yTextMapper);
    yActor.SetScale(0.04, .04, .04);
    yActor.SetPosition(Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY))/scaleXY, -0.05, -0.05);
    yActor.GetProperty().SetColor(1, 0, 0);

    vtkVectorText zText = new vtkVectorText();
    zText.SetText(Z_AXIS);

    vtkPolyDataMapper zTextMapper = new vtkPolyDataMapper();
    zTextMapper.SetInput(zText.GetOutput());

    zActor = new vtkFollower();
    zActor.SetMapper(zTextMapper);
    zActor.SetScale(.04, .04, .04);
    zActor.SetPosition(-0.05, -0.05, Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY))/scaleXY);
    zActor.GetProperty().SetColor(0, 1, 0);
  }


  public static void createTextXY() {
	  for (int i=1; i<Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY));i++){
		  double d = (i*scaleXY/10);
		  int num  = (int) d;
		  int num2 = (int) ((d-num)*10);
		  String str = num +"." +num2;
	    	vtkVectorText xText1 = new vtkVectorText();
	        xText1.SetText(str);
	        vtkPolyDataMapper xTextMapper1 = new vtkPolyDataMapper();
	        xTextMapper1.SetInput(xText1.GetOutput());
	       
	        xTextActor[i] = new vtkFollower();
	        xTextActor[i].SetMapper(xTextMapper1);
	        xTextActor[i].SetScale(0.02, .02, .02);
	        xTextActor[i].SetPosition(-0.05, (Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY)))*
	        		(i/Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY)))/10, -0.05);
	        xTextActor[i].GetProperty().SetColor(0, 0, 0);
	        ren.AddActor(xTextActor[i]);
	        xTextActor[i].SetCamera(camera);	        
	  }
	  for (int i=1; i<Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY));i++){	   
		  double d = (i*scaleXY/10);
		  int num  = (int) d;
		  int num2 = (int) ((d-num)*10);
		  String str = num +"." +num2;
		  
	    	vtkVectorText xText1 = new vtkVectorText();
	        xText1.SetText(str);
	        vtkPolyDataMapper xTextMapper1 = new vtkPolyDataMapper();
	        xTextMapper1.SetInput(xText1.GetOutput());
	       
	        yTextActor[i] = new vtkFollower();
	        yTextActor[i].SetMapper(xTextMapper1);
	        yTextActor[i].SetScale(0.02, .02, .02);
	        yTextActor[i].SetPosition( (Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY)))*
	        		(i/Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY)))/10,-0.05, -0.05);
	        yTextActor[i].GetProperty().SetColor(0, 0, 0);
	        ren.AddActor(yTextActor[i]);
	        yTextActor[i].SetCamera(camera);	        
	  }
  }
  public static void createTextDepth() {
	  for (int i=1; i<Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY));i++){	    	
	    	vtkVectorText xText1 = new vtkVectorText();
	        xText1.SetText(""+(int) Math.round(i*scaleDepth/10));
	        vtkPolyDataMapper xTextMapper1 = new vtkPolyDataMapper();
	        xTextMapper1.SetInput(xText1.GetOutput());
	       
	        zTextActor[i] = new vtkFollower();
	        zTextActor[i].SetMapper(xTextMapper1);
	        zTextActor[i].SetScale(0.02, .02, .02);
	        zTextActor[i].SetPosition(-0.05, -0.05,(Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY)))*
	        		(i/Math.max(util.getMax(ReadCSV.arrayX),util.getMax(ReadCSV.arrayY)))/10);
	        zTextActor[i].GetProperty().SetColor(0, 0, 0);
	        ren.AddActor(zTextActor[i]);
	        zTextActor[i].SetCamera(camera);	        
	  }
  }
  
  public static void createImage() {
	  vtkBMPReader bmpReader = new vtkBMPReader();
	  bmpReader.SetFileName("endu2.bmp");
	  vtkTexture atext = new vtkTexture();
	  atext.SetInput(bmpReader.GetOutput());
	  atext.InterpolateOn();
 
	  // Create a plane source and actor. The vtkPlanesSource generates
	  // texture coordinates.
	  vtkPlaneSource plane = new vtkPlaneSource();
	  vtkPolyDataMapper planeMapper = new vtkPolyDataMapper();
	  planeMapper.SetInput(plane.GetOutput());
	  planeActor = new vtkActor();
	  planeActor.SetMapper(planeMapper);
	  planeActor.SetTexture(atext);
	  planeActor.GetProperty().SetOpacity(0.5);
///	  plane.SetResolution(1000, 1);
//	  plane.SetCenter(0.5, 0.5, 0);
	  plane.SetOrigin(0, 0, 0);
	  plane.SetPoint1(0,19/scaleXY,0);
	  plane.SetPoint2(17/scaleXY,0,0);
	  
//	  plane.GetOrigin();
//	  System.out.println("plane.GetOrigin() "+ plane.GetOrigin()[0] + " " +plane.GetOrigin()[1]+ " " + plane.GetOrigin()[2]);
//	  System.out.println("plane.GetPoint1() "+ plane.GetPoint1()[0] + " " +plane.GetPoint1()[1]+ " " + plane.GetPoint1()[2]);
//	  System.out.println("plane.GetPoint2() "+ plane.GetPoint2()[0] + " " +plane.GetPoint2()[1]+ " " + plane.GetPoint2()[2]);			  
// Add the actors to the renderer, set the background and size
	   ren.AddActor(planeActor);
	   
  }
  
  
  public static void createPlane(double d1,double d2,double d3) {
	  vtkPlaneSource plane = new vtkPlaneSource();
	  vtkPolyDataMapper planeMapper = new vtkPolyDataMapper();
	  planeMapper.SetInput(plane.GetOutput());
	  planeActor2 = new vtkActor();
	  planeActor2.SetMapper(planeMapper);
	  planeActor2.GetProperty().SetOpacity(0.5);
	  planeActor2.GetProperty().SetColor(0,0,1);
///	  plane.SetResolution(1000, 1);
//	  plane.SetCenter(0.5, 0.5, 0);
	  plane.SetOrigin(0, 0, d1);
	  plane.SetPoint1(0,19/scaleXY,d2);
	  plane.SetPoint2(17/scaleXY,0,d3);
	  
	   ren.AddActor(planeActor2);
	   
	   vtkSphereSource s = new vtkSphereSource();
	   s.SetRadius(0.02);
	   s.SetCenter(0, 0, d1);  
	   vtkPolyDataMapper sphereMapper = new vtkPolyDataMapper();
	   sphereMapper.SetInputConnection( s.GetOutputPort() );
	   planeSphereActor1 = new vtkActor();
	   planeSphereActor1.SetMapper(sphereMapper );
	   planeSphereActor1.GetProperty().SetColor(0, 1, 0);
	   ren.AddActor( planeSphereActor1 );
		
	   vtkSphereSource s2 = new vtkSphereSource();
	   s2.SetRadius(0.02);
	   s2.SetCenter(0,19/scaleXY,d2);  
	   vtkPolyDataMapper sphereMapper2 = new vtkPolyDataMapper();
	   sphereMapper2.SetInputConnection( s2.GetOutputPort() );
	   planeSphereActor2 = new vtkActor();
	   planeSphereActor2.SetMapper(sphereMapper2 );
	   planeSphereActor2.GetProperty().SetColor(1, 1, 0);
	   ren.AddActor( planeSphereActor2 );
	
	   vtkSphereSource s3 = new vtkSphereSource();
	   s3.SetRadius(0.02);
	   s3.SetCenter(17/scaleXY,0,d3);  
	   vtkPolyDataMapper sphereMapper3 = new vtkPolyDataMapper();
	   sphereMapper3.SetInputConnection( s3.GetOutputPort() );
	   planeSphereActor3 = new vtkActor();
	   planeSphereActor3.SetMapper(sphereMapper3 );
	   planeSphereActor3.GetProperty().SetColor(1, 0, 0);
	   ren.AddActor( planeSphereActor3 );
	
    }

  
}

