/* 
 GeoGebra - Dynamic Mathematics for Everyone
 http://www.geogebra.org

 This file is part of GeoGebra.

 This program is free software; you can redistribute it and/or modify it 
 under the terms of the GNU General Public License as published by 
 the Free Software Foundation.

 */

package geogebra.euclidian;

import geogebra.kernel.GeoAngle;
import geogebra.kernel.GeoButton;
import geogebra.kernel.GeoElement;
import geogebra.kernel.GeoPoint;
import geogebra.kernel.GeoText;
import geogebra.kernel.GeoTextField;
import geogebra.kernel.Kernel;
import geogebra.kernel.arithmetic.ExpressionNode;
import geogebra.kernel.arithmetic.FunctionalNVar;
import geogebra.main.Application;
import geogebra.util.Unicode;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JTextField;

/**
 * Checkbox for free GeoBoolean object.
 * 
 * @author Michael
 * @version
 */
public final class DrawTextField extends Drawable {

	private final GeoButton geoButton;

	private boolean isVisible;

	private boolean hit = false;
	private String oldCaption;

	JTextField textField;
	JLabel label;
	ButtonListener bl;
	Container box = Box.createHorizontalBox();

	public DrawTextField(EuclidianView view, GeoTextField geoButton) {
		this.view = view;
		this.geoButton = geoButton;
		geo = geoButton;

		// action listener for checkBox
		bl = new ButtonListener();
		textField = geoButton.getTextField(view.getViewID());// new JTextField(20);
		label = new JLabel("Label");
		label.setLabelFor(textField);
		textField.setVisible(true);
		label.setVisible(true);
		textField.addFocusListener(bl);
		label.addMouseListener(bl);
		label.addMouseMotionListener(bl);
		textField.addKeyListener(bl);
		box.add(label);
		box.add(textField);
		view.add(box);

		update();
	}

	private class ButtonListener implements MouseListener, MouseMotionListener, FocusListener, KeyListener {

		private boolean dragging = false;
		private final EuclidianController ec = view.getEuclidianController();

		/**
		 * Handles click on check box. Changes value of GeoBoolean.
		 */
		public void itemStateChanged(ItemEvent e) {
		}

		public void mouseDragged(MouseEvent e) {

			dragging = true;
			e.translatePoint(box.getX(), box.getY());
			ec.mouseDragged(e);
			view.setToolTipText(null);
		}

		public void mouseMoved(MouseEvent e) {

			e.translatePoint(box.getX(), box.getY());
			ec.mouseMoved(e);
			view.setToolTipText(null);
		}

		public void mouseClicked(MouseEvent e) {

			if (e.getClickCount() > 1) {
				return;
			}

			e.translatePoint(box.getX(), box.getY());
			ec.mouseClicked(e);
		}

		public void mousePressed(MouseEvent e) {

			dragging = false;
			e.translatePoint(box.getX(), box.getY());
			ec.mousePressed(e);
		}

		public void mouseReleased(MouseEvent e) {

			if (!dragging && !e.isMetaDown() && !e.isPopupTrigger()
					&& (view.getMode() == EuclidianView.MODE_MOVE)) {
				// handle LEFT CLICK
				// geoBool.setValue(!geoBool.getBoolean());
				// geoBool.updateRepaint();
				// geo.runScript();
				//

				// make sure itemChanged does not change
				// the value back my faking a drag
				dragging = true;
			} else {
				// handle right click and dragging
				e.translatePoint(box.getX(), box.getY());
				ec.mouseReleased(e);
			}

		}

		public void mouseEntered(MouseEvent arg0) {
			if (!textField.hasFocus()) {
				hit = true;
				view.setToolTipText(null);
				updateText();
			}
		}

		public void mouseExited(MouseEvent arg0) {
			hit = false;
		}

		public void focusGained(FocusEvent e) {
			view.getEuclidianController().textfieldHasFocus(true);
			updateText();

		}

		public void focusLost(FocusEvent e) {
			view.getEuclidianController().textfieldHasFocus(false);

			GeoElement linkedGeo = ((GeoTextField) geo).getLinkedGeo();

			if (linkedGeo != null) {

				String defineText = textField.getText();

				if (linkedGeo.isGeoLine()) {

					// not y=
					// and not Line[A,B]
					if ((defineText.indexOf('=') == -1) && (defineText.indexOf('[') == -1)) {
						// x + 1 changed to
						// y = x + 1
						defineText = "y=" + defineText;
					}

					String prefix = linkedGeo.getLabel() + ":";
					// need a: in front of
					// X = (-0.69, 0) + \lambda (1, -2)
					if (!defineText.startsWith(prefix)) {
						defineText = prefix + defineText;
					}
				} else if (linkedGeo.isGeoText()) {
					defineText = "\"" + defineText + "\"";
				} else if (linkedGeo.isGeoPoint()) {
					if (((GeoPoint) linkedGeo).toStringMode == Kernel.COORD_COMPLEX) {
						// z=2 doesn't work for complex numbers (parses to GeoNumeric)
						defineText = defineText + "+0" + Unicode.IMAGINARY;
					}
				} else if (linkedGeo instanceof FunctionalNVar) {
					// string like f(x,y)=x^2
					// or f(\theta) = \theta
					defineText = linkedGeo.getLabel() + "(" + ((FunctionalNVar) linkedGeo).getVarString()
							+ ")=" + defineText;
				}

				try {
					linkedGeo = geo.getKernel().getAlgebraProcessor()
							.changeGeoElementNoExceptionHandling(linkedGeo, defineText, false, true);
				} catch (Exception e1) {
					geo.getKernel().getApplication().showError(e1.getMessage());
					updateText();
					return;
				}
				((GeoTextField) geo).setLinkedGeo(linkedGeo);

				updateText();

			}

			geo.runScripts(textField.getText());

		}

		public void keyPressed(KeyEvent e) {
			// TODO Auto-generated method stub

		}

		public void keyReleased(KeyEvent e) {
			if (e.getKeyChar() == '\n') {
				// geo.runScripts(textField.getText());

				// this should be enough to trigger script event
				// ie in focusLost
				view.requestFocus();
			} else if (e.getKeyCode() != KeyEvent.VK_DELETE && e.getKeyCode() != KeyEvent.VK_BACK_SPACE){
				GeoElement linkedGeo = ((GeoTextField) geo).getLinkedGeo();
				
				if (linkedGeo instanceof GeoAngle) {
					
					String text = textField.getText();
					
					// return if text already contains degree symbol or variable
					for (int i = 0 ; i < text.length() ; i++) {
						if (!Character.isDigit(text.charAt(i))) return;
					}
					
					int caretPos = textField.getCaretPosition();
					
					// add degree symbol to end if it's (a) a GeoText and (b) just digits
					textField.setText(text+Unicode.degree);
					
					textField.setCaretPosition(caretPos);
				}
			}

		}

		public void keyTyped(KeyEvent e) {
			// TODO Auto-generated method stub

		}

	}

	private void updateText() {

		GeoElement linkedGeo = ((GeoTextField) geo).getLinkedGeo();
		if (linkedGeo != null) {

			String text;

			if (linkedGeo.isGeoText()) {
				text = ((GeoText) linkedGeo).getTextString();
			} else {

				// want just a number for eg a=3 but we want variables for eg y=m x + c
				boolean substituteNos = linkedGeo.isGeoNumeric() && linkedGeo.isIndependent();
				text = linkedGeo.getFormulaString(ExpressionNode.STRING_TYPE_GEOGEBRA, substituteNos);
			}

			if (linkedGeo.isGeoText() && (text.indexOf("\n") > -1)) {
				// replace linefeed with \\n
				while (text.indexOf("\n") > -1) {
					text = text.replaceAll("\n", "\\\\\\\\n");
				}
			}
			if (!textField.getText().equals(text)) { // avoid redraw error
				textField.setText(text);
			}

		}

		((GeoTextField) geo).setText(textField.getText());

	}

	@Override
	final public void update() {
		isVisible = geo.isEuclidianVisible();
		// textField.setVisible(isVisible);
		// label.setVisible(isVisible);
		box.setVisible(isVisible);
		if (!isVisible) {
			return;
		}

		// show hide label by setting text
		if (geo.isLabelVisible()) {
			// get caption to show r
			String caption = geo.getCaption();
			if (!caption.equals(oldCaption)) {
				oldCaption = caption;
				labelDesc = GeoElement.indicesToHTML(caption, true);
			}
			label.setText(labelDesc);
		} else {
			label.setText("");
		}

		int fontSize = view.fontSize + geoButton.getFontSize();
		Application app = view.getApplication();

		Font vFont = view.getFont();
		Font font = app.getFontCanDisplay(textField.getText(), false, vFont.getStyle(), fontSize);

		textField.setOpaque(true);
		label.setOpaque(false);
		textField.setFont(font);
		label.setFont(font);
		textField.setForeground(geo.getObjectColor());
		label.setForeground(geo.getObjectColor());
		Color bgCol = geo.getBackgroundColor();
		textField.setBackground(bgCol != null ? bgCol : view.getBackground());

		textField.setFocusable(true);
		textField.setEditable(true);
		updateText();

		box.validate();

		xLabel = geo.labelOffsetX;
		yLabel = geo.labelOffsetY;
		Dimension prefSize = box.getPreferredSize();
		labelRectangle.setBounds(xLabel, yLabel, prefSize.width, prefSize.height);
		box.setBounds(labelRectangle);
	}

	private void updateLabel() {
		/*
		 * xLabel = geo.labelOffsetX; yLabel = geo.labelOffsetY;
		 * 
		 * labelRectangle.setBounds(xLabel, yLabel, ((textSize == null) ? 0 : textSize.x), 12);
		 */

	}

	@Override
	final public void draw(Graphics2D g2) {
		if (isVisible) {
			if (geo.doHighlighting()) {
				label.setOpaque(true);
				label.setBackground(Color.lightGray);

			} else {
				label.setOpaque(false);
			}
		}
	}

	/**
	 * Removes button from view again
	 */
	final public void remove() {
		view.remove(box);
	}

	/**
	 * was this object clicked at? (mouse pointer location (x,y) in screen coords)
	 */
	@Override
	final public boolean hit(int x, int y) {
		return hit;
	}

	@Override
	final public boolean isInside(Rectangle rect) {
		return rect.contains(labelRectangle);
	}

	/**
	 * Returns false
	 */
	@Override
	public boolean hitLabel(int x, int y) {
		return false;
	}

	@Override
	final public GeoElement getGeoElement() {
		return geo;
	}

	@Override
	final public void setGeoElement(GeoElement geo) {
		this.geo = geo;
	}

}
