/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.ui.attribute.editor;


import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;

import org.jboss.tools.common.MethodNotImplementedException;
import org.jboss.tools.common.model.ui.widgets.BorderedControl;
import org.jboss.tools.common.model.ui.widgets.IWidgetSettings;
import org.jboss.tools.common.model.ui.widgets.ScrolledComposite;
import org.jboss.tools.common.model.ui.widgets.border.Border;
 
public class StringFieldEditor extends ExtendedFieldEditor {

	public static final int VALIDATE_ON_KEY_STROKE = 0;
	public static final int VALIDATE_ON_FOCUS_LOST = 1;
	public static int UNLIMITED = -1;
	private boolean isValid;
	private String oldValue;
	private Text textField; // real text control
	private Control textControl; // maybe border control with text control inside
	private int charCapacity = UNLIMITED;
	private int textLimit = UNLIMITED;
	private String error;
	private boolean emptyStringAllowed = true;
	private int validateStrategy= VALIDATE_ON_KEY_STROKE;
	
	public StringFieldEditor() {}

	public StringFieldEditor(IWidgetSettings settings) {
		super(settings);
	}

	protected void adjustForNumColumns(int numColumns) {
		GridData gd = (GridData)textField.getLayoutData();
		gd.horizontalSpan = numColumns - 1;
		// We only grab excess space if we have to
		// If another field editor has more columns then
		// we assume it is setting the width.
		gd.grabExcessHorizontalSpace = gd.horizontalSpan == 1;
	}

	protected boolean validate() {
		boolean result = false;
		if (emptyStringAllowed) result = true;
		if (textField == null) {
			result = false;
			return result;
		}
		String txt = textField.getText();
		if (txt == null) {
			result = false;
		} else {
			result = (txt.trim().length() > 0) || emptyStringAllowed;
		}
		result = result && doValidate();
		if (result)
			clearErrorMessage();
		else
			showErrorMessage(error);
		return result;
	}

	protected boolean doValidate() {
		return true;
	}

	protected void doFillIntoGrid(Composite parent, int numColumns) {
		getLabelComposite(parent);
	
		createTextControl(parent);
		GridData gd = new GridData();
		gd.horizontalSpan = numColumns - 1;
		if (charCapacity != UNLIMITED) {
			GC gc = new GC(textField);
			try {
				Point extent = gc.textExtent("X");//$NON-NLS-1$
				gd.widthHint = charCapacity * extent.x;
			} finally {
				gc.dispose();
			}
		} else {
			gd.horizontalAlignment = GridData.FILL;
			gd.grabExcessHorizontalSpace = true;
		}
		textField.setLayoutData(gd);
	}

	protected void doLoad() {
		throw new MethodNotImplementedException();
	}

	protected void doLoadDefault() {
		throw new MethodNotImplementedException();
	}

	protected void doStore() {
		getPreferenceStore().setValue(getPreferenceName(), textField.getText());
	}

	public int getNumberOfControls() {
		return 2;
	}

	public String getStringValue() {
		if (textField != null)
			return textField.getText();
		else
			return getPreferenceStore().getString(getPreferenceName());
	}

	protected Text getTextField() {
		return textField;
	}
	
	protected Control getTextControl() {
		return textControl;
	}
	
	public Control createTextControl(Composite parent) {
		///do not restore this assignment! glory
		///textControl = textField;
		if (textField == null) {
			int style = getSettings().getStyle("Text.Style"); //$NON-NLS-1$
			if (style==SWT.DEFAULT) style = SWT.NONE;
///			Color bg = getSettings().getColor("Text.Background");
			Color fg = getSettings().getColor("Text.Foreground"); //$NON-NLS-1$
			Font font = getSettings().getFont("Text.Font"); //$NON-NLS-1$
			Border border = getSettings().getBorder("Text.Border"); //$NON-NLS-1$
			boolean readOnly = isAlwaysReadOnly();
			if (border != null) {
				if(readOnly) style |= SWT.READ_ONLY;
				BorderedControl borderedControl = new BorderedControl(parent, SWT.NONE, border);
				textField = new Text(borderedControl, style);
				textControl = borderedControl;
			} else {
				int style2 = SWT.BORDER;
				if(readOnly) style2 |= SWT.READ_ONLY;
				textField = new Text(parent, style2);
				textControl = textField;
			}
			textField.setFont(font);
///			textField.setBackground(bg);
			textField.setForeground(fg);

			switch (validateStrategy) {
				case VALIDATE_ON_KEY_STROKE :
					textField.addModifyListener(new ModifyListener() {
						public void modifyText(ModifyEvent e) {
							valueChanged();
						}						
					});
	
					textField.addFocusListener(new FocusAdapter() {
						public void focusGained(FocusEvent e) {
							refreshValidState();
						}
						public void focusLost(FocusEvent e) {
							clearErrorMessage();
						}
					});
					break;
				case VALIDATE_ON_FOCUS_LOST :
					textField.addKeyListener(new KeyAdapter() {
						public void keyPressed(KeyEvent e) {
							clearErrorMessage();
						}
					});
					textField.addFocusListener(new FocusAdapter() {
						public void focusGained(FocusEvent e) {
							refreshValidState();
						}
						public void focusLost(FocusEvent e) {
							valueChanged();
							clearErrorMessage();
						}
					});
					break;
				default :
					Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$
			}
			textField.addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					textField = null;
				}
			});
			if(textLimit > 0){//Only set limits above 0 - see SWT spec
				textField.setTextLimit(textLimit);
			}

			addContentAssist(textField);

		} else {
			checkParent(textControl, parent);
		}
		//SampleContentAssistentProcessor �ontentProcessor = new SampleContentAssistentProcessor();
		//ControlContentAssistHelper.createTextContentAssistant(getTextField(), �ontentProcessor);
		textField.addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e) {
				if(textField == null || textField.isDisposed()) return;
				ScrolledComposite.scrollToVisible(textField, new Rectangle(0, 0, textField.getBounds().width, textField.getBounds().height));
			}
			public void focusLost(FocusEvent e) {
			}
		});
		return textControl;
	}

	protected void addContentAssist(Text text) {}

	
	/**
	 * We cannot controls to provide READ-ONLY property on the fly.
	 * Instead, let us at least create it as READ-ONLY when it has
	 * no chance to get editable, for instance, if object to edit 
	 * is from jar.
	 * @return
	 */
	protected boolean isAlwaysReadOnly() {
		return false;
	}
	
	public boolean isEmptyStringAllowed() {
		return emptyStringAllowed;
	}

	public boolean isValid() {
		return isValid;
	}

	protected void refreshValidState() {
		isValid = validate();
	}

	public void setEmptyStringAllowed(boolean b) {
		emptyStringAllowed = b;
	}

	public void setErrorMessage(String message) {
		error = message;
	}

	public void setFocus() {
		if (textField != null) {
			textField.setSelection(0, textField.getText().length());
			textField.setFocus();
		}
	}

	public void setStringValue(String value) {
		if (textField != null) {
			if (value == null)
				value = "";//$NON-NLS-1$
			oldValue = textField.getText();
			if (!oldValue.equals(value)) {
				String ov = oldValue;
				Point p = textField.getSelection();
				textField.setText(value);
				int i = findPosition(ov, value, p.x);
				textField.setSelection(i);
				valueChanged();
			}
		}
	}
	
	private int findPosition(String ov, String nv, int p) {
		if(p == ov.length() || p < 0) return nv.length();
		for (int i = 0; i < p; i++) {
			if(i >= ov.length() || i >= nv.length()) return nv.length();
			if(ov.charAt(i) != nv.charAt(i)) return i;			
		}
		return p;		
	}

	public void setTextLimit(int limit) {
		textLimit = limit;
		if (textField != null)
			textField.setTextLimit(limit);
	}

	public void setValidateStrategy(int value) {
		Assert.isTrue(value == VALIDATE_ON_FOCUS_LOST || value == VALIDATE_ON_KEY_STROKE);
		validateStrategy = value;
	}

	protected void valueChanged() {
		setPresentsDefaultValue(false);
		boolean oldState = isValid;
		refreshValidState();
	
		if (isValid != oldState)
			fireStateChanged(IS_VALID, oldState, isValid);

		if(textField == null) {
			//disposed
			return;
		}
		String newValue = textField.getText();
		if (!newValue.equals(oldValue)) {
			fireValueChanged(VALUE, oldValue, newValue);
			if(textField == null) {
				//disposed while firing
				return;
			}
			oldValue = textField.getText();
		}
	}

	public void setEnabled(boolean enabled){
		super.setEnabled(enabled);
		setTextControlEnabled(enabled);
		updateErrorState();
	}
	
	protected void setTextControlEnabled(boolean enabled) {
		Control c = getTextControl();
		if (c != null && !c.isDisposed()) {
			Text text = getTextField();
			if(text != null && !text.isDisposed()) {
				text.setEditable(enabled);
			} else {
				boolean e = isAlwaysReadOnly();
				c.setEnabled(enabled || e);
			}			
		}
	}
	
	public void cut() {
		if (textField != null 
				&& textField.isFocusControl()
				&& isEnabled()) {
			textField.cut();
			valueChanged();
		}
	}

	public void copy() {
		if (textField != null && textField.isFocusControl()) {
			textField.copy();
		}
	}

	public void paste() {
		if (textField != null 
				&& textField.isFocusControl()
				&& isEnabled()) {
			textField.paste();
			valueChanged();
		}
	}

	public void delete() {
	}
}
