/**
* ParaTache : parallelisme de tache
* Calcul et affichage de l'image du crane et de la peau
* Le calcul s'effectue sur N processeurs : chacun realise un calcul particulier :
* Le processeur maitre (0) va lire les donnees les découper et les envoyer aux 
* autres processeurs. Chacun des processeurs va effectuer son calcul puis
* envoyer son résultat au maitre. Le maitre pourra ainsi reconstruire l'image
*
* @author Simon Lafosse
* @version 20/07/2004
*/


import java.io.*;
import java.util.*;

import vtk.util.VtkPanelContainer;
import vtk.util.VtkPanelUtil;
import vtk.util.VtkUtil;
import vtk.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

import mpi.*;

public class ParaDataN extends JComponent implements VtkPanelContainer, ActionListener {

  private vtkPanel renWin;
  private vtkVolume16Reader v16;
  static Color CPeau2;
  static Color CPeau;
  static Color CFond;
  private vtkActor skin;
  private vtkActor skin2;
  private Transform conversion = new Transform();
  
   static { 
    	try {
      	System.loadLibrary("vtkPatentedJava");
    	} catch (Throwable e) {
      	System.out.println("cannot load vtkPatented");
    	}
    }

  public ParaDataN(int myrank, MPI mpi, int nbproc)throws MPIException{
 
    renWin = new vtkPanel();
    
    //tags des messages envoyes aux esclaves (de 150 a 150+nbproc) 
    int tag1 = 50; 
    int tag2 = 150; 
    
    //tags des messages envoyes au maitre (de 350 a 350+nbproc) 
    int tag3 = 250;
    int tag4 = 350;
       
    if (myrank==0){ 
    
	/************************/
	/* 1 lecture des donnees*/
	/************************/
	
    	v16 = new vtkVolume16Reader();
    	v16.SetDataDimensions(64, 64);
    	v16.SetDataByteOrderToLittleEndian();
    	v16.SetFilePrefix(VtkUtil.getVtkDataRoot() + "/Data/headsq/quarter");
    	v16.SetImageRange(1, 93);
    	v16.SetDataSpacing(3.2, 3.2, 1.5);
 
 
	/******************************************
	/*  2. Decoupe des donnees en vol1 et vol2
	/******************************************/
	
	vtkImageData img =  v16.GetOutput();
	img.Update();
	
	//on recupere les dimensions de l'image dans dims
    	int[] dims = img.GetDimensions();
    
    	//dimensions de chaque tranche de l'image
	int interval = dims[0]/nbproc;
	
    	//on cree vol1 qui va recevoir la moitie de l'image (image coupee selon le plan yz)
    	vtkExtractVOI vol1 = new vtkExtractVOI();
    	vol1.SetInput(v16.GetOutput());
    	vol1.SetVOI(0, interval, 0, dims[1], 0, dims[2]);
    	vol1.Update(); 

	//pour les esclaves
	for (int rankp=1; rankp<nbproc; rankp++){
	 
	 	// creation nv volume
		vtkExtractVOI vol2 = new vtkExtractVOI();
		vol2.SetInput(v16.GetOutput());
		vol2.SetVOI((rankp*interval), (rankp+1)*interval, 0, dims[1], 0, dims[2]);
		vol2.Update(); 
	
		/*********************************
		/*  3. Envoie de vol2 a l esclave
		/*********************************/
	
    		double[] v1 = conversion.dataToByte(vol2.GetOutput()); 
 
    		int[] tlong = new int[1];
    		tlong[0] = v1.length;	
    		mpi.COMM_WORLD.Send(tlong, 0, 1, MPI.INT, rankp, tag1+rankp);
		System.out.println("processeur 0 : message " + (tag1+rankp) + " envoye");
   		mpi.COMM_WORLD.Send(v1, 0, v1.length, MPI.DOUBLE, rankp, tag2+rankp);
		System.out.println("processeur 0 : message " + (tag2+rankp) + " envoye");
		}
	
	
	/*********************************
	/*  4. Calcul sur ma partie
	/*********************************/
	
    	vtkContourFilter skinExtractor = new vtkContourFilter();
    	skinExtractor.SetInput(vol1.GetOutput());
    	skinExtractor.SetValue(0, 500);
    	vtkPolyDataNormals skinNormals = new vtkPolyDataNormals();
    	skinNormals.SetInput(skinExtractor.GetOutput());
    	skinNormals.SetFeatureAngle(60.0);
    	vtkPolyDataMapper skinMapper = new vtkPolyDataMapper();
    	skinMapper.SetInput(skinNormals.GetOutput());
    	skinMapper.ScalarVisibilityOff();
    	
	skin = new vtkActor();
    	skin.SetMapper(skinMapper);
    	skin.GetProperty().SetColor(0, 1, 0);
   
	
	/******************************************
	/*  5. je recupere les travaux des esclaves
	/*****************************************/
	
	for (int rankp2=1; rankp2<nbproc; rankp2++){
    		
		//System.out.println("test" + (tag3+rankp2));
		
		int[] tlong3 = new int[1];
    		mpi.COMM_WORLD.Recv(tlong3, 0, 1, MPI.INT, rankp2, tag3+rankp2);
		System.out.println("processeur 0 : message " + (tag3+rankp2) + " recu");

    		double[] v4 = new double[tlong3[0]];	
    		mpi.COMM_WORLD.Recv(v4, 0, v4.length, MPI.DOUBLE, rankp2, tag4+rankp2);
		System.out.println("processeur 0 : message " + (tag4+rankp2) + " recu");

    		vtkPolyData skinData = conversion.byteToDataP(v4);
	
		vtkPolyDataNormals skinNormals2 = new vtkPolyDataNormals();
    		skinNormals2.SetInput(skinData);
    		skinNormals2.SetFeatureAngle(60.0);
    		vtkPolyDataMapper skinMapper2 = new vtkPolyDataMapper();
    		skinMapper2.SetInput(skinNormals2.GetOutput());
    		skinMapper2.ScalarVisibilityOff();
	
		double color = (double)rankp2/(double)nbproc;
    		
		//System.out.println("processeur de rang " + rankp2 + " color : " + color);

		skin2 = new vtkActor();
    		skin2.SetMapper(skinMapper2);
    		skin2.GetProperty().SetColor(0, color, 0);
		
  		renWin.GetRenderer().AddActor(skin2);
  		}
  	
    	/****************************
	/*  6. J'affiche le resultat
	/****************************/
	
	vtkOutlineFilter outlineData = new vtkOutlineFilter();
    	outlineData.SetInput(img);
    	vtkPolyDataMapper mapOutline = new vtkPolyDataMapper();
    	mapOutline.SetInput(outlineData.GetOutput());
    	vtkActor outline = new vtkActor();
    	outline.SetMapper(mapOutline);
    	outline.GetProperty().SetColor(0, 0, 0);
	
   	vtkCamera aCamera = new vtkCamera();
   	aCamera.SetViewUp(0, 0, -1);
   	aCamera.SetPosition(0, 1, 0);
   	aCamera.SetFocalPoint(0, 0, 0);
   	aCamera.ComputeViewPlaneNormal();
    
    	//renWin.GetRenderer().AddActor(skin2);
    	renWin.GetRenderer().AddActor(skin);
    	renWin.GetRenderer().SetActiveCamera(aCamera);
    	renWin.GetRenderer().ResetCamera();
    	aCamera.Dolly(1.5);
	
    	renWin.GetRenderer().SetBackground(1, 1, 1);
    	VtkPanelUtil.setSize(renWin, 640, 480);

    	renWin.GetRenderer().ResetCameraClippingRange();

    	setLayout(new BorderLayout());
	add(renWin, BorderLayout.CENTER);
	
    }
    
    if(myrank!=0){ 
    
    	/*******************************************************************
	/*
	/* je suis un esclave :
	/*	- 1. je recupere ma partie des donnees
	/*	- 2. j'effectue les calculs sur ma partie
	/*	- 3. je renvoie le travail au maitre
	/*
	/*******************************************************************/

    	int[] tlong = new int[1];
    	mpi.COMM_WORLD.Recv(tlong, 0, 1, MPI.INT, 0, tag1+myrank);
	System.out.println("processeur " + myrank + " : message " + (tag1+myrank) + " recu");

    	double[] v2 = new double[tlong[0]];	
    	mpi.COMM_WORLD.Recv(v2, 0, v2.length, MPI.DOUBLE, 0, tag2+myrank);
	System.out.println("processeur " + myrank + " : message " + (tag2+myrank) + " recu");

    	vtkImageData vol2 = conversion.byteToData(v2);
	vol2.Update();
	
	vtkContourFilter skinExtractor2 = new vtkContourFilter();
   	skinExtractor2.SetInput(vol2);
    	skinExtractor2.SetValue(0, 500);
	
    	double[] v3 = conversion.dataToByte(skinExtractor2.GetOutput()); 
    	int[] tlong2 = new int[1];
    	tlong2[0] = v3.length;
	
	//System.out.println("esclave" + (tag3+myrank));
	
    	mpi.COMM_WORLD.Send(tlong2, 0, 1, MPI.INT, 0, (tag3+myrank));
	System.out.println("processeur " + myrank + " : message " + (tag3+myrank) + " envoye");
	
   	mpi.COMM_WORLD.Send(v3, 0, v3.length, MPI.DOUBLE, 0, tag4+myrank);
	System.out.println("processeur " + myrank + " : message " + (tag4+myrank) + " envoye");

    }  
  }
 

 public void actionPerformed(ActionEvent e) {
  String MName;
  MName = ((JMenuItem)e.getSource()).getText();
   
    
  if (MName.equals("Quit")) {
    System.out.println("AU REVOIR . . . . . . ");
    System.exit(0);
    }
    
  if (MName.equals("Back")) {
  CFond=JColorChooser.showDialog(this,"Selectionner la couleur du fond",CFond);
  if (CFond != null)
    renWin.GetRenderer().SetBackground(CFond.getRed()/255.,
                                       CFond.getGreen()/255.,
				       CFond.getBlue()/255.);
  }
  
  if (MName.equals("Skin 1")) {
  CPeau=JColorChooser.showDialog(this,"Selectionner la couleur de la peau droite",CPeau);
  if (CPeau!= null)
    skin.GetProperty().SetColor(CPeau.getRed()/255.,
                                CPeau.getGreen()/255.,
				CPeau.getBlue()/255.);
  }
  
  if (MName.equals("Skin 2")) {
  CPeau2=JColorChooser.showDialog(this,"Selectionner la couleur de la peau gauche",CPeau2);
  if (CPeau2!= null)
    skin2.GetProperty().SetColor(CPeau2.getRed()/255.,
                                 CPeau2.getGreen()/255.,
				 CPeau2.getBlue()/255.);
  }
  
  }
  


  public vtkPanel getRenWin() {
    return renWin;
  }


  public static void main(String args[]) {
    try{
    MPI mpi = new MPI();
    mpi.Init(args);
    int myrank=mpi.COMM_WORLD.Rank(); //id du processeur
    int nbproc=mpi.COMM_WORLD.Size(); // Nb de processeurs
    double tempsdebut = mpi.Wtime();
    
    ParaDataN panel = new ParaDataN(myrank, mpi, nbproc);
    
    if(myrank==0){ // si je suis le maitre j'affiche le resultat
    
    	JFrame frame = new JFrame("Medical1");	
	
	JMenuBar mb = new JMenuBar();
  	JMenu fileMenu = new JMenu("File", false);
  	JMenu optionMenu = new JMenu("Options", true);
  	JMenuItem jq = new JMenuItem("Quit");
  	JMenuItem jf = new JMenuItem("Back");
  	JMenuItem js = new JMenuItem("Skin 1");
  	JMenuItem jsk = new JMenuItem("Skin 2");
	
  	JPopupMenu.setDefaultLightWeightPopupEnabled(false);    
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 	 
  	frame.setJMenuBar(mb);
  	mb.add(fileMenu);
  	mb.add(optionMenu);
  	
  	fileMenu.addSeparator();
  	fileMenu.add(jq);
  
  	optionMenu.add(jf);
  	optionMenu.add(js);
    	optionMenu.add(jsk);
	
  	jq.addActionListener(panel);
  	jf.addActionListener(panel);
  	js.addActionListener(panel);
	jsk.addActionListener(panel);
	
    	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	frame.getContentPane().add("Center", panel);
    	frame.pack();
    	frame.setVisible(true);
	
	double tempsfin = mpi.Wtime();
	
	System.out.println("Temps total pour obtenir l'image = " + (tempsfin - tempsdebut) +" secondes");
	}
    }
    catch(MPIException me){
    }
  }
}

