

import java.awt.Component;
import java.util.Vector;

import javax.media.j3d.BoundingBox;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;

import org.cellmicrocosmos.cm2.membranepackingalgorithm.Atom;
import org.cellmicrocosmos.cm2.membranepackingalgorithm.Lipid;
import org.cellmicrocosmos.cm2.membranepackingalgorithm.MembranePackingAlgorithm;
import org.cellmicrocosmos.cm2.membranepackingalgorithm.MembranePackingAlgorithmGUI;
import org.cellmicrocosmos.cm2.membranepackingalgorithm.Molecule;
import org.cellmicrocosmos.cm2.membranepackingalgorithm.Protein;


/**
 * This tool algorithm boxifies molecules.
 * 
 * @author Bjoern Sommer bjoern@CELLmicrocosmos.org
 *
 */
public class MoleculeBoxifier extends MembranePackingAlgorithm {

	// indicates if lipids should be deleted if intersect
	boolean boxifyLipids;
	boolean boxifyProteins;

	// a flag that can be toggled to abort the algorithm
	boolean stop = false;
	
	int userSelectedDomain = -1;

	/*
	 * This method gets invoked before the fillWithLipids() process is started
	 * This is the point where you should present your GUI, like i'm doing here
	 */
	public boolean buildAndShowGui() {

		// check if the user selected a membrane
		if (this.getSelectedArea() != -1) {

			int val = JOptionPane
					.showConfirmDialog(
							null,
							"You have currently selected a particular area.\nDo you want to consider this area only?\n",
							"Selected Area", JOptionPane.YES_NO_OPTION,
							JOptionPane.QUESTION_MESSAGE);

			if (val == JOptionPane.YES_OPTION) {
				userSelectedDomain = this.getSelectedArea();
			}

		}
		
		// building up the options panel for the GUI
		final JPanel controlPanel = new JPanel();
		final JLabel infoLipids = new JLabel("Boxify ");
		final JCheckBox checkBoxLipids = new JCheckBox("Membrane Lipids");
		checkBoxLipids.setSelected(true);
		final JCheckBox checkBoxProteins = new JCheckBox("Proteins");
		checkBoxProteins.setSelected(true);

		controlPanel.add(infoLipids);
		controlPanel.add(checkBoxLipids);
		controlPanel.add(checkBoxProteins);
		
		/*
		 * showing the GUI to the user. The returnvalue indicates if the user
		 * confirmed or cancelled the dialog so far
		 */
		final boolean returnValue = MembranePackingAlgorithmGUI.popUpGUI(controlPanel,
				"Molecule Boxifier", "Press (Ctrl+r) when algorithm finished");

		// returning false if all should be cancelled
		if (returnValue == false)
			return false;

		this.boxifyLipids = checkBoxLipids.isSelected();
		this.boxifyProteins = checkBoxProteins.isSelected();

		/*
		 * the user settings are ok plus the user must have confirmed if the
		 * thread reaches this code, so true gets returned in order to proceed
		 */
		return true;

	}

	/*
	 * This method gets called when the algorithm gets started. Remember: The
	 * algorithm finishes when the thread leaves this method
	 */
	public void fillWithLipids() throws Exception {

		int counter = 0;
		
		this.setDisplayedProgress(0);
		
		/*
		 * starting a loop that runs until the terminal condition is reached or
		 * the stop flag has been set on true
		 */
		while (this.stop == false) {

			Vector<Molecule> molecules = new Vector<Molecule>();
			
			if (counter ==0 && boxifyLipids) {
				this.displayInformation("Boxify Lipids Extracellular.");
				Vector<Lipid> lipids = this.getAllLipidsInDomain(userSelectedDomain, MembranePackingAlgorithm.EXTRA);
				this.setDisplayedProgress(33);
				molecules.addAll(lipids);
			} else if (counter ==1 && boxifyLipids) {
				this.displayInformation("Boxify Lipids Intracellular.");
				Vector<Lipid> lipids = this.getAllLipidsInDomain(userSelectedDomain, MembranePackingAlgorithm.INTRA);
				this.setDisplayedProgress(66);
				molecules.addAll(lipids);
			} else if (counter ==2 && boxifyProteins) {
				this.displayInformation("Boxify Proteins.");
				Vector<Protein> proteins = this.getAllProteinsInDomain(userSelectedDomain);
				this.setDisplayedProgress(100);
				molecules.addAll(proteins);
			}
			
			if (molecules != null) {
				for (Molecule m : molecules) {
					boxifyMolecule(m);
				}
			}
			
			counter ++;
			if (counter==3)
				this.stop = true;
			
		}

		this.displayInformation("Finished.");

	}
	
	/**
	 * Boxify the given Molecule.
	 * @param m Molecule to boxify
	 * @return
	 */
	public void boxifyMolecule(Molecule m) {
		BoundingBox bb = m.getBounds();
		
		Point3d upper = new Point3d();
		bb.getUpper(upper);
		Point3f upperF = new Point3f(upper);
		
		Point3d lower = new Point3d();
		bb.getLower(lower);
		Point3f lowerF = new Point3f(lower);
		
		Atom[] atomList = m.getOriginalAtoms();
		int counter = 0;
		
		for(int i=0;i<atomList.length;i++) {
			
			switch (counter) {
				// upper area
				case 0: atomList[i].setPosition(new Point3f(upperF.x,upperF.y,upperF.z)); break;
				case 1: atomList[i].setPosition(new Point3f(lowerF.x,upperF.y,upperF.z)); break;
				case 2: atomList[i].setPosition(new Point3f(lowerF.x,upperF.y,lowerF.z)); break;
				case 3: atomList[i].setPosition(new Point3f(upperF.x,upperF.y,lowerF.z)); break;
				// lower area
				case 4: atomList[i].setPosition(new Point3f(upperF.x,lowerF.y,upperF.z)); break;
				case 5: atomList[i].setPosition(new Point3f(lowerF.x,lowerF.y,upperF.z)); break;
				case 6: atomList[i].setPosition(new Point3f(lowerF.x,lowerF.y,lowerF.z)); break;
				case 7: atomList[i].setPosition(new Point3f(upperF.x,lowerF.y,lowerF.z)); break;
				
				default:break;
			}

			if (!atomList[i].getElement().equals("C"))
//			if (atomList[i].getElement().equals("N") || atomList[i].getElement().equals("O") || atomList[i].getElement().equals("P"))
				atomList[i].setPosition(new Point3f(upperF.x+((lowerF.x-upperF.x)/2),upperF.y,upperF.z+((lowerF.z-upperF.z)/2)));
			
			if (counter!=7)
				counter++;
			else
				counter=0;
			
		}
		
	}
	
	public String getFullInformation() {

		// this string is getting displayed in the textarea were the algorithms
		// are selected
		return	"<html>This algorithm boxifies all lipids and/or proteins.<br>" +
				"All atoms are shifted to the maximum positions of the<br>" +
				"bounding box. The atoms indicating the top position of<br>" +
				"the molecule are shifted to the center of the top area.<br>" +
				"<br>" +
				"Notes: To see the results, you have to press the<br>" +
				"REFRESH GEOMETRIES Button (Ctrl-r) after the<br>" +
				"algorithm is finished.<br>" +
				"If you want to use this boxes for membrane generation,<br>" +
				"do not forget to activate Shape Collision in 3D Settings.<br>" +
				"It also may be useful to activate Custom Detail Settings<br>" +
				"and to use the lowest precisions for all three sliders.<br>" +
				"<br>" +
				"Author: Bj&ouml;rn Sommer<br>" +
				"Version: 1.0</html>";
	}

	public String getName() {

		// the chosen name for this algorithm. It has to return a unique name in
		// order to get accepted
		return "Molecule Boxifier Tool";
	}

	public Component getRuntimeControlPanel() {

		// we don't provide any runtime controls for this simple algorithm
		return null;
	}

	public String getShortInformation() {

		// you can leave out some information if you want
		return "Boxifies lipids and proteins.";
	}

	public String getStringRepresentationOfSetupParameters() {

		// returning a String that describes the users setup
		return "Boxify Proteins: " + this.boxifyProteins
				+ "\n" + "Boxify Lipids: "
				+ this.boxifyLipids;
	}

	public boolean overrideExistingLipids() {

		// existing lipids may remain in this case
		return false;
	}

	public void stopAlgorithm() {

		// setting the stop flag on true, forcing the algorithm to stop at the
		// next loop
		this.stop = true;
	}
	
	public boolean supportsAbsoluteValues() {
		// this distribution mode is not important for this algorithm, 
		// therefore this is set to true
		return true;
	}

	public boolean supportsMicrodomains() {
		// this algorithm provides a full microdomain support
		return true;
	}
	
}
