package jp.sourceforge.projects.ee2e.core.editors;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;
import org.eclipse.ui.texteditor.AbstractTextEditor;

import jp.sourceforge.projects.ee2e.core.relax.*;
import jp.sourceforge.projects.ee2e.core.EE2EDebug;
import jp.sourceforge.projects.ee2e.core.EE2EPlugin;

import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Position;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.model.*;

/**
 * To change this generated comment edit the template variable "typecomment":
 * Window&gt;Preferences&gt;Java&gt;Templates.
 * To enable and disable the creation of type comments go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation.
 *
 * @author Toshikazu Ando [ ando@park.ruru.ne.jp ]
 */
public class EE2EToolkit {
	/** structure type */
	public static final String NO_TYPE = "no-type";
	public static final String INDENT = "indent";
	public static final String CURSOR = "cursor";
	public static final String KEYWORD = "keyword";
	public static final String ARGUMENT = "argument";
	public static final String REGION = "region";

	public EE2EToolkit() {
		if (toolkit != null) {
			EE2EDebug.printf(1, "not single!!");
		}
	}

	/**
	 * Method My instance.
	 * @return EE2EToolkit
	 */
	public static EE2EToolkit get() {
		if (toolkit == null) {
			toolkit = new EE2EToolkit();
		}
		return toolkit;
	}

	/** Structure is processed. */
	public void doStructure(
		EE2RStructureList structureList,
		java.util.LinkedList argList,
		EE2RTag nextTag,
		EE2EIndentBehavior indentBehavior,
		AbstractTextEditor editor,
		IDocument document,
		ITextSelection textSelection)
		throws BadLocationException {

		int offset = textSelection.getOffset();
		int saveOffset = offset;
		String region = document.get(offset, textSelection.getLength());
		document.replace(offset, textSelection.getLength(), "");
		for (int i = 0; i < structureList.getStructureCount(); i++) {
			EE2RStructure structure = structureList.getStructure(i);
			if ((structure.getType() == null)
				|| (structure.getType().equals(""))
				|| (structure.getType().equals(EE2EToolkit.NO_TYPE))) {
				StringBuffer sb = new StringBuffer(structure.getValue());
				while (0 <= sb.indexOf("\\n")) {
					int of = sb.indexOf("\\n");
					sb.replace(of, of + 2, "\n");
				}
				offset = pushString(document, offset, sb.toString());
			} else if (structure.getType().equals(EE2EToolkit.INDENT)) {
				Position saveExcursion = new Position(offset);
				document.addPosition(saveExcursion);
				indentBehavior.doIndent(offset, document);
				offset = saveExcursion.getOffset();
				document.removePosition(saveExcursion);
			} else if (structure.getType().equals(EE2EToolkit.KEYWORD)) {
				String work;
				if (nextTag == null) {
					work = structureList.getDefault();
				} else {
					work = nextTag.getName();
				}
				offset = pushString(document, offset, work);
			} else if (structure.getType().equals(EE2EToolkit.ARGUMENT)) {
				offset =
					doStructureArgument(
						structureList,
						argList,
						nextTag,
						document,
						offset);
			} else if (structure.getType().equals(EE2EToolkit.CURSOR)) {
				Position saveExcursion = new Position(offset);
				document.addPosition(saveExcursion);
				indentBehavior.doBehavior(editor, document, textSelection);
				offset = saveExcursion.getOffset();
				document.removePosition(saveExcursion);
				saveOffset = offset;
			} else if (structure.getType().equals(EE2EToolkit.REGION)) {
				offset = pushString(document, offset, region);
				region = null;
			} else {
				EE2EDebug.printf(3, "type not found!");
			}
		}
		offset = pushString(document, offset, region);
		EE2ESelectIndent sec = new EE2ESelectIndent(saveOffset);
		editor.getSelectionProvider().setSelection(sec);
	}

	/** The attribute of structure is processed. */
	protected int doStructureArgument(
		EE2RStructureList structureList,
		java.util.LinkedList argList,
		EE2RTag nextTag,
		IDocument document,
		int offset)
		throws BadLocationException {
		if ((argList == null) || (argList.size() <= 0)) {
			return offset;
		}
		String work;
		boolean flag = false;
		for (int i = 0; i < argList.size(); i++) {
			String attrValue = (String) argList.get(i);
			if ((attrValue == null) || (attrValue.equals(""))) {
				continue;
			}
			if (!flag) {
				work = structureList.getArgsep0();
				offset = pushString(document, offset, work);
			} else {
				work = structureList.getArgsep3();
				offset = pushString(document, offset, work);
			}
			flag = true;
			work = this.getAttribute(nextTag, i).getName();
			offset = pushString(document, offset, work);
			work = structureList.getArgsep2();
			offset = pushString(document, offset, work);
			work = structureList.getArgsep1();
			offset = pushString(document, offset, work);
			work = attrValue;
			offset = pushString(document, offset, work);
			work = structureList.getArgsep4();
			offset = pushString(document, offset, work);
		}
		if (flag) {
			work = structureList.getArgsep5();
			offset = pushString(document, offset, work);
		}
		return offset;
	}

	/** A character string is inserted. */
	public int pushString(IDocument document, int offset, String arg)
		throws BadLocationException {
		if ((arg == null) || (arg.equals(""))) {
			return offset;
		}
		document.replace(offset, 0, arg);
		return offset + arg.length();
	}

	/** get idocument form editor.
	* @param editor editor
	* @return idocument
	*/
	public IDocument getEditorToDocument(AbstractTextEditor editor) {
		if (editor == null) {
			EE2EDebug.printf(3, "not cont!!");
			return null;
		}
		if (editor.getDocumentProvider() == null) {
			EE2EDebug.printf(3, "not getDocumentProvider()!!");
			return null;
		}
		IDocument document =
			editor.getDocumentProvider().getDocument(editor.getEditorInput());
		return document;
	}

	/** This is a method for a test. It uses succeeding. */
	protected URL getBaseURL(AbstractUIPlugin plugin)
		throws MalformedURLException {
        URL base;
        if (plugin != null) {
            EE2EDebug.printf(3, "use default");
            base = plugin.getDescriptor().getInstallURL();
        } else {
            EE2EDebug.printf(3, "use resouce"); // for test...
            base = EE2REe2e.class.getResource("../../../../../../../");
            base = new URL(base, "../../jp.sourceforge.projects.ee2e.core/");
        }
        return base;
	}

	/**
	 * from String to ee2e.
	 * @param arg
	 * @return EE2REe2e
	 */
	public EE2REe2e getEe2e(String arg) {
		if ((this.getEe2eMap() == null) || (this.getEe2eMap().equals(""))) {
			EE2EDebug.printf(3, "arg is null");
			return null;
		}
		EE2REe2e ee2e = (EE2REe2e) this.getEe2eMap().get(arg);
		if (ee2e != null) {
			return ee2e;
		}
		try {
			AbstractUIPlugin plugin = EE2EPlugin.getDefault();
			URL base = this.getBaseURL(plugin);
            URL url = new URL(base, "useree2e/" + arg + ".ee2e");
            try {
                EE2EDebug.printf(3, "url(user)=" + url.toString());
                ee2e = new EE2REe2e(url);
            } catch (IOException userException) {
                EE2EDebug.printf(3, "IOException(user) e=" + userException.getMessage());
                url = new URL(base, "lib/" + arg + ".ee2e");
                try {
                    EE2EDebug.printf(3, "url(system)=" + url.toString());
                    ee2e = new EE2REe2e(url);
                } catch (IOException myException) {
                    EE2EDebug.printf(3, "IOException(system) e=" + myException.getMessage());
                    if( plugin == null) {
                        EE2EDebug.printf(1, "plugin null!");
                        return null;
                    }
                    IPluginDescriptor iDescriptor = plugin.getDescriptor();
                    if(! (iDescriptor instanceof PluginDescriptorModel)) {
                        EE2EDebug.printf(1, "iDescriptor not plugin!");
                        return null;
                    }
                    PluginDescriptorModel descriptor = (PluginDescriptorModel) iDescriptor;
                    PluginFragmentModel[] model = descriptor.getFragments();
                    for(int i = 0 ; (i < model.length) && (ee2e == null) ; i++) {
                        String str = model[i].getLocation();
                        base = new URL(str);
                        url = new URL(base, "lib/" + arg + ".ee2e");
                        EE2EDebug.printf(3, "file=" + base.getFile().toString());
                        try {
                            ee2e = new EE2REe2e(url);
                        } catch (IOException e2) {
                            EE2EDebug.printf(3, "IOException e2=" + e2.getMessage());
                        }
                    }
                    if (ee2e == null) {
                        EE2EDebug.printf(1, "no file in all pugin");
                        return null;
                    }
                }
            }
		} catch (MalformedURLException e) {
			EE2EDebug.printf(1, "MalformedURLException e=" + e.getMessage());
			return null;
		} catch (SAXException e) {
			EE2EDebug.printf(1, "SAXException e=" + e.getMessage());
			return null;
		} catch (ParserConfigurationException e) {
			EE2EDebug.printf(
				1,
				"ParserConfigurationException e=" + e.getMessage());
			return null;
		}
        this.getEe2eMap().put(arg, ee2e);
		for (int i = 0; i < ee2e.getRequireCount(); i++) {
			this.getEe2e(ee2e.getRequire(i).getHref());
		}
		for (int i = 0; i < ee2e.getDefvarCount(); i++) {
			EE2RDefvar defvar = ee2e.getDefvar(i);
            EE2EDebug.printf(
                5,
                "push defvar=" +defvar.getId() + " / " + ee2e.getDefvar(i));
            this.getDefvarMap().put(defvar.getId(), ee2e.getDefvar(i));
		}
		for (int i = 0; i < ee2e.getDefvarCount(); i++) {
			EE2RDefvar defvar = ee2e.getDefvar(i);
			EE2EDebug.printf(
				5,
				"defvar "
					+ i
					+ "/"
					+ ee2e.getDefvarCount()
					+ " id="
					+ defvar.getId());
			java.util.List list = new java.util.LinkedList();
			for (int j = 0; j < defvar.getContentCount(); j++) {
				IEE2RDefvarChoice choice = defvar.getContent(j);
				if (choice instanceof EE2RAttribute) {
					EE2RAttribute attr = (EE2RAttribute) choice;
					if ((attr.getRef() != null)
						&& (!attr.getRef().equals(""))) {
						list.add(getAttribute(attr.getRef()));
					} else {
						list.add(attr);
					}
				} else if (choice instanceof EE2REnumeration) {
					EE2REnumeration enum = (EE2REnumeration) choice;
					if ((enum.getRef() != null)
						&& (!enum.getRef().equals(""))) {
						EE2RDefvar nextdef = getDefvar(enum.getRef());
						if (nextdef != null) {
							list.addAll(getEnumerationSet(nextdef));
						} else {
							list.add(enum); // samename etc...
						}
					} else {
						list.add(enum);
					}
				} else if (choice instanceof EE2RTag) {
					EE2RTag tag = (EE2RTag) choice;
					if ((tag.getRef() != null) && (!tag.getRef().equals(""))) {
						EE2RDefvar nextdef = getDefvar(tag.getRef());
						if (nextdef == null) {
							EE2EDebug.printf(
								1,
								"nextref not fount=" + tag.getRef());
							return null;
						}
						for (int k = 0; k < nextdef.getContentCount(); k++) {
							if (nextdef.getContent(k) instanceof EE2RTag) {
								EE2RTag nextTag =
									(EE2RTag) nextdef.getContent(k);
								if (nextTag.getRef() == null) {
									list.add(nextTag);
								} else {
									EE2EDebug.printf(1, "tagref found!!");
									return null;
								}
							} else {
								EE2EDebug.printf(1, "not tagref!!");
								return null;
							}
						}
					} else {
						list.add(tag);
					}
				} else {
					EE2EDebug.printf(1, "choice not found!!");
					return null;
				}
			}
			defvar.setContent(
				(IEE2RDefvarChoice[]) list.toArray(new IEE2RDefvarChoice[0]));
		}
		EE2RIterationList iteList = ee2e.getIterationList();
		if (iteList != null) {
			java.util.List col = new java.util.LinkedList();
			for (int i = 0; i < iteList.getIterationCount(); i++) {
				EE2RIteration ite = iteList.getIteration(i);
				if ((ite.getRequire() != null)
					&& (!ite.getRequire().equals(""))) {
					EE2REe2e requireEe2e = getEe2e(ite.getRequire());
					if (requireEe2e == null) {
						EE2EDebug.printf(1, "requireEe2e not found!!");
						return null;
					}
					EE2RIterationList requireIteList =
						requireEe2e.getIterationList();
					if (requireIteList == null) {
						EE2EDebug.printf(1, "requireIteList not found!!");
						return null;
					}
					for (int j = 0;
						j < requireIteList.getIterationCount();
						j++) {
						EE2RIteration iteSingle =
							requireIteList.getIteration(j);
						if ((iteSingle.getOpener() == null)
							|| (iteSingle.getOpener().equals(""))) {
							continue;
						}
						col.add(iteSingle);
					}
				} else {
					col.add(ite);
				}
			}
			iteList.setIteration(
				(EE2RIteration[]) col.toArray(new EE2RIteration[0]));
		}
		EE2EDebug.printf(5, "getEe2e end");
		return ee2e;
	}
	public EE2RStructureList getStructureList(EE2REe2e ee2e, String select) {
		if ((ee2e == null) || (select == null)) {
			return null;
		}
		for (int i = 0; i < ee2e.getStructureListCount(); i++) {
			EE2RStructureList str = ee2e.getStructureList(i);
			if (str == null) {
				continue;
			}
			if (str.getType() == null) {
				continue;
			}
			if (str.getType().equals(select)) {
				return str;
			}
		}
		return null;
	}
	public EE2RDefvar getDefvar(EE2RStructureList list) {
		if (list == null) {
			return null;
		}
		return (EE2RDefvar) this.getDefvarMap().get(list.getTagref());
	}
	public EE2RDefvar getDefvar(String ref) {
		if (ref == null) {
			return null;
		}
		return (EE2RDefvar) this.getDefvarMap().get(ref);
	}
	public EE2RTag getTag(EE2RDefvar defvar, String tagName) {
		if (defvar == null) {
			EE2EDebug.printf(3, "defvar not found");
			return null;
		}
		for (int i = 0; i < defvar.getContentCount(); i++) {
			IEE2RDefvarChoice choice = defvar.getContent(i);
			if (!(choice instanceof EE2RTag)) {
				continue;
			}
			EE2RTag tag = (EE2RTag) choice;
			if ((tag.getName() != null) && (tag.getName().equals(tagName))) {
				return tag;
			}
		}
		return null;
	}
	public java.util.Collection getTagSet(EE2RDefvar defvar) {
		if (defvar == null) {
			EE2EDebug.printf(3, "defvar not found");
			return null;
		}
		java.util.TreeSet set = new java.util.TreeSet();
		for (int i = 0; i < defvar.getContentCount(); i++) {
			IEE2RDefvarChoice choice = defvar.getContent(i);
			if (!(choice instanceof EE2RTag)) {
				continue;
			}
			EE2RTag tag = (EE2RTag) choice;
			if (tag.getName() == null) {
				EE2EDebug.printf(1, "tagFailer id=" + defvar.getId());
			} else {
				set.add(tag.getName());
			}
		}
		return set;
	}
	public java.util.Collection getEnumerationSet(EE2RDefvar defvar) {
		if (defvar == null) {
			EE2EDebug.printf(3, "defvar not found");
			return null;
		}
		java.util.TreeSet set = new java.util.TreeSet();
		for (int i = 0; i < defvar.getContentCount(); i++) {
			IEE2RDefvarChoice choice = defvar.getContent(i);
			if (!(choice instanceof EE2RTag)) {
				continue;
			}
			EE2RTag tag = (EE2RTag) choice;
			set.add(tag.getName());
		}
		return set;
	}
	public EE2RAttribute getAttribute(EE2RTag tag, int index) {
		if ((tag == null)
			|| (index < 0)
			|| (tag.getAttributeCount() <= index)) {
			EE2EDebug.printf(3, "imput error");
			return null;
		}
		EE2RAttribute attribute = tag.getAttribute(index);
		if ((attribute.getRef() != null) && (!attribute.getRef().equals(""))) {
			attribute = getAttribute(attribute.getRef());
		}
		return attribute;
	}
	public EE2RAttribute getAttribute(String id) {
		EE2RDefvar defvar = getDefvar(id);
		if (defvar == null) {
			EE2EDebug.printf(3, "defvar not found");
			return null;
		}
		if (1 != defvar.getContentCount()) {
			EE2EDebug.printf(3, "not choice");
			return null;
		}
		IEE2RDefvarChoice choice = defvar.getContent(0);
		if (!(choice instanceof EE2RAttribute)) {
			EE2EDebug.printf(3, "instanceof error");
			return null;
		}
		return (EE2RAttribute) choice;
	}
	public String replaceFileSeparator(String input) {
		StringBuffer buff = new StringBuffer(input);
		for (int j = 0; j < buff.length(); j++) {
			if (buff.charAt(j) == '\\') {
				buff.setCharAt(j, '/');
			}
		}
		return buff.toString();
	}
    public java.util.Map getEe2eMap() {
        return this.ee2eMap;
    }
    protected java.util.Map getDefvarMap() {
        return this.defvarMap;
    }
    public void clearEe2eMap() {
        this.getEe2eMap().clear();
        this.getDefvarMap().clear();
    }
	/** myInstance */
	private static EE2EToolkit toolkit;

    /**  ee2e ee2eMap information */
	private java.util.Map ee2eMap = new java.util.HashMap();

    /** defvar ee2eMap infrmation */
	private java.util.Map defvarMap = new java.util.HashMap();
}
