/******************************************************************************* 
 * Copyright (c) 2009-2011 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: 
 * Red Hat, Inc. - initial API and implementation 
 ******************************************************************************/ 
package org.jboss.tools.common.text;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.eclipse.jface.resource.ImageDescriptor;

/**
 * Text Proposal for Content Assist.
 * @author Alexey Kazakov
 */
public class TextProposal {

	public interface PostProcessing {
		public void process(TextProposal proposal, String value, int offset);
	}

	public static final int R_NONE = 0;
	public static final int R_JSP_JSF_EL_VARIABLE_ATTRIBUTE_VALUE = 810;
	public static final int R_JSP_ATTRIBUTE_VALUE = 830;
	public static final int R_XML_ATTRIBUTE_VALUE = 850;
	public static final int R_XML_ATTRIBUTE_NAME = 910;
	public static final int R_TAG_INSERTION = 1210;				// This value was changed from 500 to 1210 because it seems that according WTP's value was risen up to 1200 in 3.6 
	public static final int R_TAG_TEMPLATE = 1250;
	public static final int R_XML_ATTRIBUTE_VALUE_TEMPLATE = 91;
	public static final int R_XML_TAG_INSERTION = 91;
	
	public static final int R_CLOSE_TAG = 1550;
	
	private static final long serialVersionUID = 3257007635692926512L;

	private Object source;
	private String label;
	private String contextInfo;
	private ImageDescriptor imageDescriptor;
	private boolean emptyImage = true;
	private String replacementString;
	private List<String> alternativeMatches = new ArrayList<String>();
	private boolean emptyContextInfo = true;
	private int relevance = R_NONE;
	private int position = -1;
	private boolean autoActivationContentAssistantAfterApplication = false;
	
	private String type;
	private String sourceType;
	
	private int start = -1;
	private int end = -1;

	PostProcessing postProcessing;

	private IExecutableTextProposal executable = null;
	private boolean isFilterable = true;

	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@Override
	public Object clone() throws CloneNotSupportedException {
		TextProposal newObject = new TextProposal();
		newObject.source = this.source;
		newObject.label = this.label;
		newObject.contextInfo = this.contextInfo;
		newObject.imageDescriptor = this.imageDescriptor;
		newObject.emptyImage = this.emptyImage;
		newObject.replacementString = this.replacementString;
		newObject.emptyContextInfo = this.emptyContextInfo;
		newObject.relevance = this.relevance;
		newObject.position = this.position;
		newObject.autoActivationContentAssistantAfterApplication = this.autoActivationContentAssistantAfterApplication;
		newObject.type = this.type;
		newObject.sourceType = this.sourceType;
		newObject.start = this.start;
		newObject.end = this.end;
		return newObject;
	}

	public String getType() {
		return type;
	}
	
	public void setType(String typeName) {
		this.type = typeName;
	}
	
	public String getSourceType() {
		return sourceType;
	}
	
	public void setSourceType(String sourceTypeName) {
		this.sourceType = sourceTypeName;
	}
	
	/**
	 * 
	 * @return
	 */
    public int getRelevance() {
        return relevance;
    }

    /**
     * 
     * @param relevance
     */
    public void setRelevance(int relevance) {
        this.relevance = relevance;
    }

	/**
	 * @return
	 */
	public String getContextInfo() {
		return contextInfo;
	}

	/**
	 * @return 
	 */
	public ImageDescriptor getImageDescriptor() {
		return imageDescriptor;
	}

//	CommonUIPlugin.getImageDescriptorRegistry().get
	/**
	 * @return
	 */
	public String getLabel() {
		String l = label != null ? label : getReplacementString() == null ? "" : getReplacementString(); //$NON-NLS-1$
	
		StringBuilder dispLabel = new StringBuilder(l);
		if (type != null) {
			dispLabel.append(" : ");  //$NON-NLS-1$
			dispLabel.append(type);
		}
		if (sourceType != null) {
			dispLabel.append(" - ");  //$NON-NLS-1$
			dispLabel.append(sourceType);
		}
		
		return dispLabel.toString();
	}

	/**
	 * @return
	 */
	public String getReplacementString() {
		return replacementString;
	}

	/**
	 * @param string
	 */
	public void setContextInfo(String string) {
		contextInfo = string;
		if(contextInfo != null) {
			emptyContextInfo = false; 
		}
	}

	/**
	 * @param string
	 */
	public void setImageDescriptor(ImageDescriptor img) {
		this.imageDescriptor = img;
		if(this.imageDescriptor != null) {
			emptyImage = false; 
		}
	}

	/**
	 * @param string
	 */
	public void setLabel(String string) {
		label = string;
	}

	/**
	 * @param string
	 */
	public void setReplacementString(String string) {
		replacementString = string;
	}

	/**
	 * @return
	 */
	public boolean hasContextInfo() {
		return !emptyContextInfo;
	}

	/**
	 * @return
	 */
	public boolean hasImage() {
		return !emptyImage;
	}

	/**
	 * @return
	 */
	public int getPosition() {
		if(position==-1 && getReplacementString()!=null) {
			return getReplacementString().length();
		}
		return position;
	}

	/**
	 * @param i
	 */
	public void setPosition(int i) {
		position = i;
	}

	/**
	 * 
	 * @param lowerCase
	 */
	public void changeCase(boolean lowerCase) {
		if(lowerCase) {
			if(label!=null) label = label.toLowerCase();
			if(replacementString!=null) replacementString = replacementString.toLowerCase();
		} else {
			if(label!=null) label = label.toUpperCase();
			if(replacementString!=null) replacementString = replacementString.toUpperCase();
		}
	}

	/**
	 * 
	 *
	 */
	public void removeAutocompleteRequiredAttributes() {
		int endAttr = replacementString.lastIndexOf('"');
		if(endAttr!=-1) {
			int startAttr = replacementString.substring(0, replacementString.indexOf('"')).lastIndexOf(' ');
			String newReplacementString = replacementString.substring(0, startAttr);
			if(endAttr+1<replacementString.length()) {
				newReplacementString = newReplacementString + replacementString.substring(endAttr + 1);
			}
			replacementString = newReplacementString;
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isCloseTag() {
		return label != null && label.startsWith("/"); //$NON-NLS-1$
	}

	/**
	 * Returns object with apply() method that should be invoked by
	 * AutoContentAssistantProposal.apply() instead super.apply().
	 * 
	 * @return
	 */
	public IExecutableTextProposal getExecutable() {
		return executable;
	}

	public void setExecutable(IExecutableTextProposal executable) {
		this.executable = executable;
	}

	/**
	 * Returns true, if this proposal may be removed by the filter
	 * that removes duplicated proposals.
	 * 
	 * @return
	 */
	public boolean isFilterable() {
		return isFilterable;
	}

	public void setFilterable(boolean value) {
		isFilterable = value;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		StringBuilder buffer = new StringBuilder();
		buffer.append("label: "); //$NON-NLS-1$
		buffer.append(label);
		buffer.append("\ncontextInfo: "); //$NON-NLS-1$
		buffer.append(contextInfo);
		buffer.append("\nreplacementString: "); //$NON-NLS-1$
		buffer.append(replacementString);

		return buffer.toString();
	}

	/**
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(Object o) {
		return label.compareTo(((TextProposal)o).getLabel());
	}

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {
		if(obj == this) {
			return true;
		}
		if((!(obj instanceof TextProposal)) || (obj == null)) {
			return false;
		}
		TextProposal anotherProposal = (TextProposal)obj;
		boolean labelB = false;
		boolean contextInfoB = false;
		boolean replacementStringB = false;
		if(this.label!=null) {
		    labelB = this.label.equals(anotherProposal.getLabel());
		} else if(anotherProposal.getLabel()==null) {
		    labelB = true;
		}
		if(this.contextInfo!=null) {
		    contextInfoB = this.contextInfo.equals(anotherProposal.getContextInfo());
		} else if(anotherProposal.getContextInfo()==null) {
		    contextInfoB = true;
		}
		if(this.replacementString!=null) {
		    replacementStringB = this.replacementString.equals(anotherProposal.getReplacementString());
		} else if(anotherProposal.getReplacementString()==null) {
		    replacementStringB = true;
		}
		return labelB&&contextInfoB&&replacementStringB;
    }

    /**
     * 
     * @return
     */
	public boolean autoActivationContentAssistantAfterApplication() {
		return autoActivationContentAssistantAfterApplication;
	}

	/**
	 * 
	 */
	public void setAutoActivationContentAssistantAfterApplication(boolean autoActivationContentAssistantAfterApplication) {
		this.autoActivationContentAssistantAfterApplication = autoActivationContentAssistantAfterApplication;
	}

	public void setStart(int n) {
		start = n;
	}

	public void setEnd(int n) {
		end = n;
	}

	public int getStart() {
		return start;
	}

	public int getEnd() {
		return end;
	}

	public void setPostProcessing(PostProcessing postProcessing) {
		this.postProcessing = postProcessing;
	}

	public void postProcess(String value, int offset) {
		if(postProcessing != null) postProcessing.process(this, value, offset);
	}

    /**
	 * @return the source
	 */
	public Object getSource() {
		return source;
	}

	/**
	 * @param source the source to set
	 */
	public void setSource(Object source) {
		this.source = source;
	}

	/**
	 * Returns the first alternative match of the list assigned to this proposal,
	 * or null if the list is empty.
	 * @return
	 */
	public String getAlternateMatch() {
		return alternativeMatches.isEmpty() ? null : alternativeMatches.get(0);
	}

	/**
	 * Sets the first alternative match.
	 * 
	 * @param alternateMatch
	 */
	public void setAlternateMatch(String alternateMatch) {
		alternativeMatches.remove(alternateMatch);
		alternativeMatches.add(0, alternateMatch);
	}

	/**
	 * Returns all keywords related to this proposal
	 * @return
	 */
	public List<String> getAlternativeMatches() {
		return alternativeMatches;
	}

	/**
	 * Checks if template is matching start of any keywords for this proposals.
	 * @param template
	 * @return
	 */
	public boolean isAlternativeMatchStart(String template) {
		if(template.length() > 0) {
			for (String s: getAlternativeMatches()) {
				if(s.startsWith(template)) {
					return true;
				}
			}
		}
		return false;
	}

	public static final Comparator<TextProposal> KB_PROPOSAL_ORDER = new TextProposalComparator();

    private static class TextProposalComparator implements Comparator<TextProposal> {
		public int compare(TextProposal p1, TextProposal p2) {
			int n1=p1.replacementString.length(), n2=p2.replacementString.length();
			for (int i1=0, i2=0; i1<n1 && i2<n2; i1++, i2++) {
				char c1 = p1.replacementString.charAt(i1);
				char c2 = p2.replacementString.charAt(i2);
				if (c1 != c2) {
					c1 = Character.toUpperCase(c1);
					c2 = Character.toUpperCase(c2);
					if (c1 != c2) {
					   c1 = Character.toLowerCase(c1);
					   c2 = Character.toLowerCase(c2);
					   if (c1 != c2) {
					       return c1 - c2;
					   }
					}
				}
			}
			return n1 - n2;
		}
	}
}