package j3deditor.bin.editor.tools;


import j3deditor.bin.components.InputDialog;

import j3deditor.bin.editor.GeometryEditor;
import j3deditor.bin.editor.util.GeometryEditorListener;

import j3deditor.bin.hierarchy.GeometryManipulator;

import j3deditor.bin.hierarchy.util.NormalCalculator;

import j3deditor.bin.util.ProgressIndicator;
import j3deditor.bin.util.Utilities;

import java.awt.BorderLayout;

import java.awt.Color;
import java.awt.GridLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;


/**
 * Shows geometry info and povides interface for
 * manipulating geometry.
 * 
 * @author Risto Seene
 */
public class GeometryPanel extends JPanel implements GeometryEditorListener{

	private GeometryManipulator gm;

	private JLabel vertCount;

	private JPopupMenu popup;

	private GeometryEditor ge;

	private JMenuItem delete;

	private JMenuItem flip;

	private JMenuItem flat;

	private JMenuItem smooth1;

	private JMenuItem smooth2;


	/**
	 * Creates an instance of <code>GeometryPanel</code>.
	 * 
	 * @param gm
	 * 	geometry this panel is meant for
	 */
	public GeometryPanel(GeometryManipulator gm){
		this.gm = gm;
		this.setOpaque(false);
		initContent();
		initMenu();
	}

	/**
	 * Initializes the content of this panel.
	 */
	private void initContent(){
		this.setToolTipText(Utilities.translator.translate(this.getClass(), "rightClick"));
		this.setLayout(new BorderLayout());
		JPanel panel = new JPanel();
		panel.setOpaque(false);
		this.add(panel, BorderLayout.NORTH);
		panel.setLayout(new GridLayout(6, 1));

		JLabel geometry = new JLabel(this.gm.getGeometry().toString(), JLabel.CENTER);
		panel.add(geometry);

		vertCount = new JLabel("", JLabel.CENTER);
		vertCount.setToolTipText(Utilities.translator.translate(this.getClass(), "vertInfo"));
		panel.add(vertCount);

		panel.add(new JLabel());

		String texture = Utilities.translator.translate(this.getClass(), "texture");
		if(!gm.hasTexture()){
			texture = texture + " NO";
		}
		else {
			texture = texture + " TEXTURE " + gm.getGeometry().getTexCoordType() + "D";
		}
		panel.add(new JLabel(texture, JLabel.RIGHT));

		String color = Utilities.translator.translate(this.getClass(), "color");
		if(gm.getGeometry().hasColor()){
			color = color + " COLOR " + gm.getGeometry().getColorType();
		}
		else {
			color = color + " NO";
		}
		panel.add(new JLabel(color, JLabel.RIGHT));

		String normals = Utilities.translator.translate(this.getClass(), "normals");
		if(gm.getGeometry().hasNormals()){
			normals = normals + " YES";
		}
		else {
			normals = normals + " NO";
		}
		panel.add(new JLabel(normals, JLabel.RIGHT));

		updateInfo();
	}

	/**
	 * Updates geometry info.
	 */
	private void updateInfo(){
		int verts = this.gm.getVertexCount();
		int allVerts = this.gm.getAllVertCount();

		vertCount.setText(verts + "(" + allVerts + ") " + Utilities.translator.translate(this.getClass(), "vertices"));
	}

	/**
	 * Initializes popup menu.
	 */
	private void initMenu(){
		popup = new JPopupMenu();
		JMenu menu;
		JMenuItem item;

		menu = new JMenu(Utilities.translator.translate(this.getClass(), "vertices"));
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "hide"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.hideSelectedVertices();
						geometryUpdated();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "show"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.showAllVertices();
						geometryUpdated();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		menu.addSeparator();
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "editClr"));
		item.setEnabled(false);
		if(this.gm.getGeometry().hasColor()){
			item.setEnabled(true);
		}
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						String title = Utilities.translator.translate(GeometryPanel.class, "editClr");
						Color color = gm.getVertexColor();
						Color newColor = new JColorChooser().showDialog(new JFrame(), title, color);
						if(newColor != null){
							gm.setVertexColor(newColor);
						}
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "editAlpha"));
		item.setEnabled(false);
		if(this.gm.getGeometry().getColorType() == 4){
			item.setEnabled(true);
		}
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						float alpha = gm.getVertexAlpha();
						alpha = InputDialog.askFloat(alpha);
						gm.setVertexAlpha(alpha);
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		menu.addSeparator();
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "editR"));
		item.setEnabled(false);
		if(this.gm.getGeometry().getTexCoordType() > 2){
			item.setEnabled(true);
		}
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						float coord = gm.getTexCoord(2);
						coord = InputDialog.askFloat(coord);
						gm.setTexCoord(coord, 2);
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
	          item = new JMenuItem(Utilities.translator.translate(this.getClass(), "editQ"));
	          item.setEnabled(false);
	          if(this.gm.getGeometry().getTexCoordType() > 3){
	                    item.setEnabled(true);
	          }
	          item.addActionListener(new ActionListener(){

	                              public void actionPerformed(ActionEvent e){
	                                        try{
	                                                  float coord = gm.getTexCoord(3);
	                                                  coord = InputDialog.askFloat(coord);
	                                                  gm.setTexCoord(coord, 3);
	                                        } catch(Throwable t){
						Utilities.out.throwWarning(t);
	                                        }
	                              }
	                    });
	          menu.add(item);
		menu.addSeparator();
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "merge"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.mergeSelectedVertices();
						geometryUpdated();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		popup.add(menu);

		menu = new JMenu(Utilities.translator.translate(this.getClass(), "faces"));
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "select1"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.selectFaces(false);
						geometryUpdated();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "select2"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.selectFaces(true);
						geometryUpdated();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		menu.add(item);
		menu.addSeparator();
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "delete"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.deleteSelectedFaces();
						geometryUpdated();
						dataChanged(this);
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		this.delete = item;
		menu.add(item);
		menu.addSeparator();
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "flip"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						gm.flipFaces();
						geometryUpdated();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		this.flip = item;
		menu.add(item);
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "flat"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						NormalCalculator nc = new NormalCalculator(gm);
						String info = Utilities.translator.translate(this.getClass(), "flattening");
						nc.addPropertyChangeListener(new ProgressIndicator(info));
						nc.recalculateFlat();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		this.flat = item;
		menu.add(item);
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "smooth1"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						NormalCalculator nc = new NormalCalculator(gm);
						String info = Utilities.translator.translate(this.getClass(), "smoothing");
						ProgressIndicator indicator = new ProgressIndicator(info);
						indicator.setCancellableTask(nc);
						nc.addPropertyChangeListener(indicator);
						nc.setProgressChangeListener(indicator);
						nc.recalculateSmooth(true);
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		this.smooth1 = item;
		menu.add(item);
		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "smooth2"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						NormalCalculator nc = new NormalCalculator(gm);
						String info = Utilities.translator.translate(this.getClass(), "smoothing");
						ProgressIndicator indicator = new ProgressIndicator(info);
						indicator.setCancellableTask(nc);
						nc.addPropertyChangeListener(indicator);
						nc.setProgressChangeListener(indicator);
						nc.recalculateSmooth(false);
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		this.smooth2 = item;
		menu.add(item);
		popup.add(menu);

		popup.addSeparator();

		item = new JMenuItem(Utilities.translator.translate(this.getClass(), "gSize"));
		item.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						changeGeometrySize();
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		popup.add(item);

		this.addMouseListener(new MouseListener(){

				public void mouseClicked(MouseEvent e){
				}

				public void mousePressed(MouseEvent e){
				}

				public void mouseReleased(MouseEvent e){
					if(e.getButton() == MouseEvent.BUTTON3){
						popup.show(e.getComponent(), e.getX(), e.getY());
					}
				}

				public void mouseEntered(MouseEvent e){
				}

				public void mouseExited(MouseEvent e){
				}
			});
	}

	/**
	 * Changes geometry size.
	 */
	private void changeGeometrySize(){
		int newSize = ChangeGeometryDialog.askNewFaceCount(this.gm);
		if(newSize >= 0){
			this.ge.changeGeometrySize(newSize);
		}
	}

	/**
	 * Updates menu.
	 */
	private void updateMenu(){
		boolean facesSelected = this.gm.areFacesSelected();
		boolean hasNormals = this.gm.getGeometry().hasNormals();
		boolean faces = (this.gm.getGeometry().getVertexGroupMode() > 2);

		this.delete.setEnabled(facesSelected);
		this.flip.setEnabled(facesSelected);
		this.flat.setEnabled(hasNormals && facesSelected && faces);
		this.smooth1.setEnabled(hasNormals && facesSelected && faces);
		this.smooth2.setEnabled(hasNormals && facesSelected && faces);
	}

	/**
	 * Updates info and notifies listener.
	 */
	private void geometryUpdated(){
		this.updateMenu();
		if(ge != null){
			this.ge.sceneChanged(this);
		}
	}

	/**
	 * Sets a geometry event listener.
	 * 
	 * @param ge
	 * 	<code>GeometryEditor</code> which can be notified
	 *        about events
	 */
	public void setGeometryEventListener(GeometryEditor ge){
		this.ge = ge;
	}

	/**
	 * Doesn't do anything.
	 * 
	 * @param sender
	 * 	doesn't do anything
	 */
	public void sceneChanged(Object sender){
	}

	/**
	 * Updates info and menu.
	 * 
	 * @param sender
	 * 	method caller
	 */
	public void dataChanged(Object sender){
		this.updateInfo();
		this.updateMenu();
	}
}
