/*
 * (c) Copyright: Artenum SARL, 101-103 Boulevard Mac Donald,
 *                75019, Paris, France 2005.
 *                http://www.artenum.com
 *
 * License:
 *
 *  This program is free software; you can redistribute it
 *  and/or modify it under the terms of the Q Public License;
 *  either version 1 of the License.
 *
 *  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 Q Public License for more details.
 *
 *  You should have received a copy of the Q Public License
 *  License along with this program;
 *  if not, write to:
 *    Artenum SARL, 101-103 Boulevard Mac Donald,
 *    75019, PARIS, FRANCE, e-mail: contact@artenum.com
 */
package com.artenum.cassandra.plugin.defaults;

import com.artenum.cassandra.action.ActionEventInformation;
import com.artenum.cassandra.action.PluginActionListener;
import com.artenum.cassandra.pipeline.FilterImpl;
import com.artenum.cassandra.pipeline.PipeLineManager;
import com.artenum.cassandra.pipeline.CassandraObject;
import com.artenum.cassandra.pipeline.RemoveListener;
import com.artenum.cassandra.plugin.PluginManager;
import com.artenum.cassandra.plugin.defaults.ui.PickingInformationUI;
import com.artenum.cassandra.renderer.vtk.PickingObserver;

import vtk.vtkCell;
import vtk.vtkCellPicker;
import vtk.vtkDataSet;
import vtk.vtkPointPicker;
import vtk.vtkTextActor;
import vtk.vtkTextMapper;
import vtk.vtkTextProperty;

import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

/**
 * Perform a picking action on the pointed actor and return the local
 * values/informations of the related data set. Please type on p-key to activate
 * the interactor.
 * 
 * @author Julien Forest, Sebastien Jourdain, Benoit Thiebault, Jeremie Turbet
 * 
 */
public class ShowPickingInformation implements ActionListener, PickingObserver, RemoveListener {
	
	// Cassandra elements
    private String name;
    private PipeLineManager pipelineManager;
    private PluginManager pluginManager;
    private Frame owner;
    private JPopupMenu contextualMenu;

    // Contextual control GUI of the plugin
    private PickingInformationUI controlUI;
    
    // Cassandra VtkObjects for the pipeline models
    private CassandraObject cassfilter;
    private CassandraObject pointTextCassActor;
    private CassandraObject messageCassActor;
	
    // to display the information message in the 3D view
    private vtkTextMapper messageTextMapper;
    private vtkTextActor messageTextActor;
    //private StringBuffer messageTextBuffer;
    private int messageTextXPosition = 30;
    private int messageTextYPosition = 30;
    
    // to display the results in the 3D view
    
    // for the point picker
    private vtkTextMapper resultsTextMapper;
    private vtkTextActor resultTextActor;
    private StringBuffer resultsTextBuffer;
    private int resultTextXPosition = 10;
    private int resultTextYPosition = 10;
    
    private String[] coord = new String[3];
    private String padding = "                   ";
    private String[] label = new String[] { "X: ", "Y: ", "Z: " };
	private FilterImpl filterImpl;
		
	private boolean pipelineInitialised = false;
	
	private vtkDataSet pickedDataSet = null;
	
	/**
	 * The id of the mesh element id picked.
	 */
	private Integer meshElementId=-1;
	
    /**
     * main constructor
     * 
     * @param pipelineManager
     */
    //public ShowPickingInformation(PipeLineManager pipelineManager) {
    //}
    

	/**
	 * Constructor
	 */
    public ShowPickingInformation(PipeLineManager pipelineManager, PluginManager pluginManager, Frame owner) {      
        this.pluginManager = pluginManager;
        this.pipelineManager = pipelineManager;
        this.owner = owner;
        initPlugIn(this.pipelineManager, this.pluginManager, this.owner);
    }
	
    /**
     * Constructor.
     * @param pipelineManager
     * @param pluginManager
     * @param owner
     * @param initPipeline
     */
    public ShowPickingInformation(PipeLineManager pipelineManager, PluginManager pluginManager, Frame owner, int initPipeline) {
    	this.pluginManager = pluginManager;
        this.pipelineManager = pipelineManager;
        this.owner = owner;
    	initPlugIn(this.pipelineManager, this.pluginManager, this.owner);
        if (initPipeline == 1) {
            initPipeline();
        }
    }
    
    /**
     * Perform the initialisation of the plugin components. 
     * 
     * @param pipelineManager
     * @param pluginManager
     * @param owner - frame owner. 
     */
    public void initPlugIn(PipeLineManager pipelineManager, PluginManager pluginManager, Frame owner) {
        
    	// init default variable
    	this.name = "Picking";
    	this.pipelineManager = pipelineManager;
        resultsTextBuffer = new StringBuffer();
        
        // Cassandra filter macro
        filterImpl = new FilterImpl();
        filterImpl.addRemoveListener(this);
        
        //FIXME should refer to the default plugin
        //filterImpl.setParentPlugin(this);
        
        //init the control UI
        this.controlUI = new PickingInformationUI(this.pipelineManager, this, owner);
        
        
        //init the contextual popup menu
        contextualMenu = new JPopupMenu("Picking menu");
        JMenuItem showControl = new JMenuItem("Show control");
        showControl.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                controlUI.setVisible(true);
            }
        });
        contextualMenu.add(showControl);
       
        //contextualMenu.addSeparator();
        //JMenuItem remove = new JMenuItem("Remove");
        //remove.addActionListener(new ActionListener() {
        //    public void actionPerformed(ActionEvent e) {
        //        remove();
        //    }
        //});
        //contextualMenu.add(remove);
    }

    /**
     * Initialise the pipeline. 
     */
    private void initPipeline(){
    	
    	if( !pipelineInitialised ){

     	    // Load the filter in the Cassandra pipeline
            cassfilter = pipelineManager.addFilter(filterImpl, "Picking Filter");
            cassfilter.getMetaData().put(CassandraObject.POPUP_MENU, getContextualMenu());
        
            //display the information message
//            messageTextBuffer = new StringBuffer("Picking activated: press p-key to pick a mesh element.");
            messageTextMapper = new vtkTextMapper();
//            messageTextMapper.SetInput(messageTextBuffer.toString());
            vtkTextProperty messageTextProperty = messageTextMapper.GetTextProperty();
            messageTextProperty.SetFontFamilyToArial();
            messageTextProperty.SetFontSize(10);
            messageTextProperty.BoldOn();
            messageTextProperty.ShadowOn();
            messageTextProperty.SetColor(1, 0, 1);
            
            messageTextActor = new vtkTextActor();
            messageTextActor.SetMapper(messageTextMapper);
            messageTextActor.GetProperty().SetColor(1.0, 0.0, 1.0);
            this.messageTextYPosition = pipelineManager.getCassandraView().getHeight() - this.messageTextYPosition;
            messageTextActor.SetDisplayPosition(this.messageTextXPosition, this.messageTextYPosition);
            
            messageCassActor = pipelineManager.addTxtActor( messageTextActor, "Picking Message Actor" );   
            pipelineManager.setActorVisible(messageCassActor, true);
            
            //messageTextMapper.SetInput("XXXXXXXXXXXXXXXX");
            messageTextActor.SetInput("Picking activated: press p-key to pick a mesh element.");
            
            
            //load the processing actors into the Cassandra pipeline
            
            //3D rendering of result for the point picker
            resultsTextMapper = new vtkTextMapper();
            resultsTextMapper.SetInput("");
            vtkTextProperty pointTextProperty = resultsTextMapper.GetTextProperty();
            pointTextProperty.SetFontFamilyToArial();
            pointTextProperty.SetFontSize(10);
            pointTextProperty.BoldOn();
            pointTextProperty.ShadowOn();
            pointTextProperty.SetColor(1, 1, 1);
            
            resultTextActor = new vtkTextActor();
            resultTextActor.SetMapper(resultsTextMapper);
            resultTextActor.SetDisplayPosition(this.resultTextXPosition, this.resultTextYPosition);
            
            //adding the actor widget in the pipeline. 
            pointTextCassActor = pipelineManager.addTxtActor(resultTextActor, "Picking Result Actor");
            
            pipelineManager.setActorVisible(pointTextCassActor, true);

            //3D rendering for the cell picker
            
            
            // make the link with the observer for the picking action in the 
            // CassandraView class
            pipelineManager.getCassandraView().addPickingObserver(this);
            
            //display the general picking information message. 
  
            // validation of the view of the result in the 3D view
            // pipelineManager.validateViewAndGo(); // Not enough
            pipelineManager.getCassandraView().deepValidateView(); // Don t ask
                                                                 // me why,
                      // apparently needed,
                      // at least
                      // under MacOSX and default swing LAF, to activate
                      // the widget interactor.
            
            pipelineInitialised = true;
    	}
    }
    
    public double getPointPickerTolerance(){
    	return this.pipelineManager.getCassandraView().getPointPickerTolerance();
    }
    
    public void setPointPickerTolerance(double tolerance){
    	this.pipelineManager.getCassandraView().setPointPickerTolerance(tolerance);
    }
    
    public double getCellPickerTolerance(){
    	return this.pipelineManager.getCassandraView().getCellPickerTolerance();
    }
    
    public void setCellPickerTolerance(double tolerance){
    	this.pipelineManager.getCassandraView().setCellPickerTolerance(tolerance);
    }
    
    public int getPickingMode(){
    	return this.pipelineManager.getCassandraView().getPickingMode();
    }
    
    public void setPickingMode(int pickMode){
    	this.pipelineManager.getCassandraView().setPickMode(pickMode);
    }
    
    /**
     * Performs the internal action listener and update the plugin. 
     */
    public void actionPerformed(ActionEvent e) {
    	
        this.initPipeline();
        this.controlUI.update();
    }

  
    private void notifyPluginListerners(final int cellDimensionPicked){    	
    	Iterator<PluginActionListener> iter = this.pluginManager.getPluginActionListenerList().iterator();
    	ActionEventInformation actionEventInformation=new PickingActionEventInformation(this.meshElementId, this.getPickingMode(), cellDimensionPicked);
    	ActionEvent evt = new ActionEvent(actionEventInformation, 1515, "picking.action.cmd");
        while(iter.hasNext()){
        	PluginActionListener listener = iter.next();
        	listener.actionPerformed(evt);
        }
    }
    
    /**
     * performs the picking action and display in 3D view the information of the
     * picked element.
     */
    @Override
    public void pickPoint(vtkPointPicker pointPicker) {
    	    	
        if (resultTextActor != null){
        	this.meshElementId = pointPicker.GetPointId();
			if(pointPicker.GetPointId() > -1) {
        	
        	    this.pickedDataSet = pointPicker.GetDataSet();

                double[] xyz = pointPicker.GetDataSet().GetPoint(meshElementId);
            
                coord[0] = Float.toString((float) xyz[0]);
                coord[1] = Float.toString((float) xyz[1]);
                coord[2] = Float.toString((float) xyz[2]);

                // Clear buffer
                resultsTextBuffer.delete(0, resultsTextBuffer.length());

                // point ID
                resultsTextBuffer.append("Point ID: ");
                resultsTextBuffer.append(Integer.toString(meshElementId));
                resultsTextBuffer.append("\n");

                // Coord
                for (int i = 0; i < coord.length; i++) {
                    resultsTextBuffer.append(label[i % 3]);
                    resultsTextBuffer.append(coord[i]);
                    resultsTextBuffer.append(fill(coord[i]));
                }

                // data
                resultsTextBuffer.append("\nValue: ");
                if (pointPicker.GetDataSet().GetPointData().GetArray(0) != null) {
                    resultsTextBuffer.append(Double.toString(pointPicker.GetDataSet().GetPointData().GetArray(0).GetTuple1(meshElementId)));
                } else {
                    resultsTextBuffer.append("Not defined");
                }

                // set text to VTK
                resultTextActor.SetInput(resultsTextBuffer.toString());
            
            } else{
        	    System.out.println("No point found.");
        	    resultsTextBuffer.delete(0, resultsTextBuffer.length());
        	    resultsTextBuffer.append("No point found.");
        	    resultsTextBuffer.append("\n");
        	
        	    // set text to VTK
        	    //resultsTextMapper.SetInput(resultsTextBuffer.toString());
                resultTextActor.SetInput(resultsTextBuffer.toString());
                this.meshElementId=-1;
            }
        }
        
        // notify external listener
        this.notifyPluginListerners(0);//the dimension of a point is 0.
    }
    
    
	@Override
	public void pickCell(vtkCellPicker cellPicker) {
				
		// e update the targeted dataSet
		this.pickedDataSet = cellPicker.GetDataSet();
		 
		if (resultTextActor != null){

        	this.meshElementId = cellPicker.GetPointId();
			if(this.meshElementId > -1) {
				
				resultsTextBuffer.delete(0, resultsTextBuffer.length());
				resultsTextBuffer.append("Cell ID: ");
                resultsTextBuffer.append(Integer.toString(meshElementId));
                resultsTextBuffer.append("\n");
				
				// set text to VTK
                resultTextActor.SetInput(resultsTextBuffer.toString());
				
			} else {
				System.out.println("No cell found.");
        	    resultsTextBuffer.delete(0, resultsTextBuffer.length());
        	    resultsTextBuffer.append("No cell found.");
        	    resultsTextBuffer.append("\n");
        	    // set text to VTK
        	    //resultsTextMapper.SetInput(resultsTextBuffer.toString());
                resultTextActor.SetInput(resultsTextBuffer.toString());
                this.meshElementId=-1;
			}
			
		}
		
		int getCellId = cellPicker.GetCellId();
		vtkCell vtkCell = pickedDataSet.GetCell(getCellId);
		
        // notification to the external listerners
		this.notifyPluginListerners( vtkCell.GetCellDimension());
	}

    public String fill(String number) {
        return padding.substring(number.length());
    }
    
    /**
     * Get the contextual menu of the present plug-in (i.e the contextual menu linked to the filter icon 
     * in the pipeline manager).
     * 
     * @return - contextual menu of the plug-in.
     */
    public JPopupMenu getContextualMenu() {
        return contextualMenu;
    }

	@Override
	public void remove() {
		//FIXME should be done at the default plugin level
		//pluginManager.removePlugin(this);
        pipelineManager.removeVtkObject(this.pointTextCassActor);
        pipelineManager.removeVtkObject(cassfilter);		
	}

	/**
	 * Get the picked dataSet. 
	 * 
	 * @return - picked dataSet. 
	 */
	public vtkDataSet getPickedDataSet() {
		return pickedDataSet;
	}


}
