/*******************************************************************************
 * 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.jsf.ui.adopt;

import java.util.*;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.source.SourceViewer;
import org.jboss.tools.common.meta.XAdoptManager;
import org.jboss.tools.common.model.XModelException;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.util.XModelObjectLoaderUtil;
import org.jboss.tools.jsf.project.JSFNature;
import org.jboss.tools.jsf.ui.JsfUiPlugin;
import org.jboss.tools.jst.web.project.list.WebPromptingProvider;

public class JSPAdopt implements XAdoptManager {

	public boolean isAdoptable(XModelObject target, XModelObject object) {
		return (isAdoptableProperty(object) || 
            (isAdoptableNature(target) && isAdoptableBundle(object)));
	}

	protected boolean isAdoptableProperty(XModelObject object) {
		String entity = object.getModelEntity().getName();
		return "Property".equals(entity); //$NON-NLS-1$
	}

    protected boolean isAdoptableNature(XModelObject target) {
    	if(target == null) return false;
        String nature = (String)target.getModel().getProperties().get("nature"); //$NON-NLS-1$
        return JSF_NATURE_STRING.equalsIgnoreCase(nature);
    }

    protected boolean isAdoptableBundle(XModelObject object) {
        return "FilePROPERTIES".equals(object.getModelEntity().getName()); //$NON-NLS-1$
    }

	public void adopt(XModelObject target, XModelObject object, Properties p) throws XModelException {
        if(isAdoptableProperty(object)) adoptProperty(target, object, p);
        if(isAdoptableBundle(object)) adoptBundle(target, object, p);
	}

	public void adoptProperty(XModelObject target, XModelObject object, Properties p) {
		String name = object.getAttributeValue("name"); //$NON-NLS-1$
		String bundle = getBundle(object.getParent());
		int pos = -1;
		try {
			String s = p == null ? null : p.getProperty("pos"); //$NON-NLS-1$
			if(s != null && s.length() > 0) pos = Integer.parseInt(s);
		} catch (NumberFormatException e) {
			JsfUiPlugin.getPluginLog().logError(e);
		}
		SourceViewer viewer = (SourceViewer)p.get("viewer"); //$NON-NLS-1$
		if(viewer == null) {
			JsfUiPlugin.getPluginLog().logError("Viewer is null", new NullPointerException("Viewer is null")); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		}
		if(viewer.getDocument() == null) {
			return;
		}
		
		JSPTokenizer tokenizer = new JSPTokenizer();
		Token root = tokenizer.parse(viewer.getDocument());
		Token t = root.firstChild;
		while(t != null) {
			t = t.nextSibling;
		}
		
        int contextIndex = getContextIndex(tokenizer, pos);
        if(pos < 0) return;
		String jsfCorePrefix = JSPAdoptHelper.getPrefixForURI(root, JSF_CORE_TAGLIB_URI, JSF_CORE_TAGLIB_PREFIX_DEFAULT);
		String jsfHtmlPrefix = JSPAdoptHelper.getPrefixForURI(root, JSF_HTML_TAGLIB_URI, JSF_HTML_TAGLIB_PREFIX_DEFAULT);
		String prefix = "???"; //$NON-NLS-1$
		String varValue = JSPAdoptHelper.getLoadedBundleVar(root, jsfCorePrefix, bundle);
		if (varValue != null && varValue.length() > 0) prefix = varValue;

		if(prefix.equals("???")) { //$NON-NLS-1$
			WebPromptingProvider fProvider = WebPromptingProvider.getInstance();
			List<Object> l = fProvider.getList(target.getModel(), WebPromptingProvider.JSF_REGISTERED_BUNDLES, null, null);
			int map_index = 1;
			Map map = l.size() <= map_index ? null : (Map)l.get(map_index);
			if(map != null && map.containsKey(bundle)) prefix = map.get(bundle).toString();
		}
		
		tokenizer.isInTagAttributeValue(pos);
		
		if(prefix.equals("???")) { //$NON-NLS-1$
			int bp = JSPAdoptHelper.getPositionForBundle(root, jsfCorePrefix);
			if(bp < 0) return;
			prefix = JSPAdoptHelper.getNameForNewBundle(root, jsfCorePrefix);
			String loadBundle = "\n" + createLoadBundleTag(jsfCorePrefix, bundle, prefix); //$NON-NLS-1$
			try {
				viewer.getDocument().replace(bp, 0, loadBundle);
				if(pos >= bp) pos += loadBundle.length();
			} catch (BadLocationException e) {
				JsfUiPlugin.getPluginLog().logError(e);
			}
		}

        String start = getPropertyReference(prefix, name);
        if (contextIndex == 1) {
            start = "<" + jsfHtmlPrefix + ":outputText value=\"" + start + "\"/>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
		try {
			viewer.getDocument().replace(pos, 0, start);
			viewer.setSelectedRange(pos, 0);
			viewer.getTextWidget().setFocus();
		} catch (BadLocationException e) {
			JsfUiPlugin.getPluginLog().logError(e);
		}
		p.remove("start text"); //$NON-NLS-1$
	}
	
	private String getPropertyReference(String prefix, String name) {
		return (!isJavaName(name)) ? "#{" + prefix + "['" + name + "']}" : "#{" + prefix + "." + name + "}";  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
	}
	
	private boolean isJavaName(String name) {
		if(name.length() == 0) return true;
		char ch = name.charAt(0);
		if(!Character.isJavaIdentifierStart(ch)) return false;
		for (int i = 1; i < name.length(); i++) {
			ch = name.charAt(i);
			if(!Character.isJavaIdentifierPart(ch)) return false;
		}
		return true;
	}
	
	private int getContextIndex(JSPTokenizer tokenizer, int pos) {
		if(tokenizer.root == null) return -1;
		if (tokenizer.isInTagAttributeValue(pos)) return 0;
		Token e = tokenizer.getTokenAt(pos);
		if(e == null) return -1;
		if (e.kind == JSPTokenizer.TEXT || (pos == e.off)) {
			return 1;
		}
		return -1;
	}
    
    private static final String JSF_NATURE_STRING = JSFNature.NATURE_ID;
    
    private String createLoadBundleTag(String jsfCorePrefix, String bundlePath, String var) {
    	return "<" + jsfCorePrefix + ":loadBundle basename=\"" + bundlePath + "\" var=\"" + var + "\" />"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    }

	public void adoptBundle(XModelObject target, XModelObject object, Properties p) {
		if(p == null) return;
		String bundle = getBundle(object);

		int pos = -1;
		try {
			String s = p == null ? null : p.getProperty("pos"); //$NON-NLS-1$
			if(s != null && s.length() > 0) pos = Integer.parseInt(s);
		} catch (NumberFormatException e) {
			JsfUiPlugin.getPluginLog().logError(e);
		}
		SourceViewer viewer = (SourceViewer)p.get("viewer"); //$NON-NLS-1$
		
		if(viewer == null || viewer.getDocument() == null) return;

		JSPTokenizer tokenizer = new JSPTokenizer();
		Token root = tokenizer.parse(viewer.getDocument());

		int contextIndex = getContextIndex(tokenizer, pos);
		String start = bundle;
		if (contextIndex == 1) {
			String jsfCorePrefix = JSPAdoptHelper.getPrefixForURI(root, JSF_CORE_TAGLIB_URI, JSF_CORE_TAGLIB_PREFIX_DEFAULT);
			start = createLoadBundleTag(jsfCorePrefix, bundle, "|"); //$NON-NLS-1$
		}
		p.setProperty("start text", start); //$NON-NLS-1$
		p.setProperty("end text", ""); //$NON-NLS-1$ //$NON-NLS-2$
	}
    
    private final static String JSF_CORE_TAGLIB_URI = "http://java.sun.com/jsf/core"; //$NON-NLS-1$
    private final static String JSF_CORE_TAGLIB_PREFIX_DEFAULT = "f"; //$NON-NLS-1$
    private final static String JSF_HTML_TAGLIB_URI = "http://java.sun.com/jsf/html"; //$NON-NLS-1$
    private final static String JSF_HTML_TAGLIB_PREFIX_DEFAULT = "h"; //$NON-NLS-1$
    
	String getBundle(XModelObject file) {
		String bundle = XModelObjectLoaderUtil.getResourcePath(file);
		if(bundle == null) bundle = ""; //$NON-NLS-1$
		if(bundle.endsWith(".properties")) bundle = bundle.substring(0, bundle.length() - 11); //$NON-NLS-1$
		bundle = bundle.substring(1).replace('/', '.');
		int i = bundle.indexOf('_');
		if(i >= 0) bundle = bundle.substring(0, i);
		return bundle;
	}

}
