package tk.eclipse.plugin.dtdeditor.editors;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.aonir.fuzzyxml.internal.FuzzyXMLUtil;

import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;

import tk.eclipse.plugin.htmleditor.HTMLPlugin;
import tk.eclipse.plugin.htmleditor.HTMLUtil;
import tk.eclipse.plugin.htmleditor.assist.HTMLAssistProcessor;
import tk.eclipse.plugin.htmleditor.editors.HTMLSourceEditor;

/**
 * 
 * @author Naoki Takezoe
 */
public class DTDAssistProcessor extends HTMLAssistProcessor {
	
	private static final int UNDEF = 0;
	private static final int ELEMENT_ELEMENT  = 1;
	private static final int ELEMENT_TYPE     = 2;
	private static final int ELEMENT_CONTENT  = 3;
	private static final int ATTLIST_ELEMENT  = 4;
	private static final int ATTLIST_ATTRNAME = 5;
	private static final int ATTLIST_ATTRTYPE = 6;
	private static final int ATTLIST_ATTROMIT = 7;
	
	private static String[] DECLS = {"ELEMENT", "ATTLIST","ENTITY","NOTATION"};
	private static String[] ATTR_TYPES = {"CDATA","ID","IDREF","IDREFS","ENTITY","ENTITIES","NMTOKEN","NMTOKENS"};
	private static String[] ATTR_OMITS = {"#REQUIRED","#IMPLIED","#FIXED \"\"","\"\""};
	private static String[] ELEM_TYPES = {"ANY", "EMPTY", "(#PCDATA)", "()"};
	
	private Pattern elementPattern = Pattern.compile("<!ELEMENT\\s+(.*?)\\s");
	private Pattern entityPattern = Pattern.compile("<!ENTITY\\s+%\\s(.*?)\\s");
	
	private List<String> elements = new ArrayList<String>();
	private List<String> entities = new ArrayList<String>();
	
	@Override
  public void update(HTMLSourceEditor editor, String source){
		elements.clear();
		source = FuzzyXMLUtil.comment2space(source, false);
		Matcher matcher = elementPattern.matcher(source);
		while(matcher.find()){
			elements.add(matcher.group(1).trim());
		}
		
		entities.clear();
		matcher = entityPattern.matcher(source);
		while(matcher.find()){
			entities.add("%" + matcher.group(1).trim());
		}
	}
	
	@Override
  public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,int documentOffset) {
		
		String text = FuzzyXMLUtil.comment2space(viewer.getDocument().get(), false);
		text = text.substring(0, documentOffset);
		
		String word = getWord(text);
		int context = getContext(text);
		
		List<ICompletionProposal> list = new ArrayList<ICompletionProposal>();
		
		if(word.startsWith("<") || context==UNDEF){
			for(int i=0;i<DECLS.length;i++){
				if(("<!" + DECLS[i]).startsWith(word)){
					list.add(createProposal("<!" + DECLS[i] + " >", "<!" + DECLS[i] + " ... >", word, documentOffset,
							DECLS[i].length() + 3, HTMLPlugin.ICON_TAG));
				}
			}
		} else if(context==ATTLIST_ELEMENT || context==ELEMENT_CONTENT){
			for(int i=0;i<elements.size();i++){
				String element = elements.get(i);
				if(element.startsWith(word)){
					list.add(createProposal(element, element, word, documentOffset, element.length(), HTMLPlugin.ICON_ELEMENT));
				}
			}
		} else if(context==ATTLIST_ATTRTYPE){
			for(int i=0;i<ATTR_TYPES.length;i++){
				if(ATTR_TYPES[i].startsWith(word)){
					list.add(createProposal(ATTR_TYPES[i], ATTR_TYPES[i], word, documentOffset, ATTR_TYPES[i].length(), HTMLPlugin.ICON_VALUE));
				}
			}
			for(int i=0;i<entities.size();i++){
				String value = entities.get(i);
				if(value.startsWith(word)){
					list.add(createProposal(value, value, word, documentOffset, value.length(), HTMLPlugin.ICON_VALUE));
				}
			}
		} else if(context==ATTLIST_ATTROMIT){
			for(int i=0;i<ATTR_OMITS.length;i++){
				if(ATTR_OMITS[i].startsWith(word) && !word.startsWith("\"")){
					int position = ATTR_OMITS[i].indexOf('"');
					if(position < 0){
						position = ATTR_OMITS[i].length();
					} else {
						position++;
					}
					list.add(createProposal(ATTR_OMITS[i], ATTR_OMITS[i], word, documentOffset, position, HTMLPlugin.ICON_VALUE));
				}
			}
		} else if(context==ELEMENT_TYPE){
			for(int i=0;i<ELEM_TYPES.length;i++){
				if(ELEM_TYPES[i].startsWith(word)){
					int position = ELEM_TYPES[i].length();
					if(ELEM_TYPES[i].equals("()")){
						position = 1;
					}
					list.add(createProposal(ELEM_TYPES[i], ELEM_TYPES[i], word, documentOffset, position, HTMLPlugin.ICON_VALUE));
				}
			}
		}
		
		HTMLUtil.sortCompilationProposal(list);
		ICompletionProposal[] prop = list.toArray(new ICompletionProposal[list.size()]);
		return prop;
	}
	
	private static CompletionProposal createProposal(String text, String display, String word, int offset, int position, String image){
		return new CompletionProposal(
				text, offset - word.length(), word.length(), position,
				HTMLPlugin.getDefault().getImageRegistry().get(image), display, 
				null, null);
	}
	
	protected String getWord(String text){
		
		StringBuffer sb = new StringBuffer();
		
		for (int i = 0; i < text.length(); i++) {
			char c = text.charAt(i);
			if(Character.isWhitespace(c) || c=='(' || c==')' || c=='|' || c==','){
				if(sb.length() > 0){
					sb.setLength(0);
				}
			} else {
				sb.append(c);
			}
		}
		
		return sb.toString();
	}
	
	protected int getContext(String text){
		
		StringBuffer sb = new StringBuffer();
		int context = UNDEF;
		boolean flag = false;
		
		for (int i = 0; i < text.length(); i++) {
			char c = text.charAt(i);
			if((Character.isWhitespace(c) || c=='(' || c==')') && !flag){
				if(c=='(' || c==')'){ sb.append(c); }
				if(sb.length() > 0){
					context = judgeContext(sb.toString(), context);
					sb.setLength(0);
				}
			} else {
				if(c=='"'){
					flag = !flag;
				}
				sb.append(c);
			}
		}
		
		return context;
	}
	
	private int judgeContext(String word, int prev){
		if(word.equals("<!ELEMENT")){
			return ELEMENT_ELEMENT;
		} else if(word.equals("<!ATTLIST")){
			return ATTLIST_ELEMENT;
		} else if(word.startsWith("<!")){
			return UNDEF;
		} else if(prev==ELEMENT_ELEMENT){
			return ELEMENT_TYPE;
		} else if(prev==ELEMENT_TYPE && word.equals("(")){
			return ELEMENT_CONTENT;
		} else if(prev==ELEMENT_CONTENT && !word.endsWith(")")){
			return ELEMENT_CONTENT;
		} else if(prev==ATTLIST_ELEMENT){
			return ATTLIST_ATTRNAME;
		} else if(prev==ATTLIST_ATTRNAME){
			return ATTLIST_ATTRTYPE;
		} else if(prev==ATTLIST_ATTRTYPE){
			return ATTLIST_ATTROMIT;
		} else if(prev==ATTLIST_ATTROMIT){
			if(word.startsWith("#FIXED")){
				return ATTLIST_ATTROMIT;
			} else {
				return ATTLIST_ATTRNAME;
			}
		}
		return UNDEF;
	}

	/* (non-Javadoc)
	 * @see tk.eclipse.plugin.htmleditor.assist.HTMLAssistProcessor#enableTemplate()
	 */
	@Override
  public boolean enableTemplate() {
		return false;
	}
}
