/**
 * Copyright 2010 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.eclipse.editors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.drools.compiler.DroolsParserException;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.debug.core.IDroolsDebugConstants;
import org.drools.lang.descr.AttributeDescr;
import org.drools.lang.descr.BaseDescr;
import org.drools.lang.descr.FactTemplateDescr;
import org.drools.lang.descr.FunctionDescr;
import org.drools.lang.descr.FunctionImportDescr;
import org.drools.lang.descr.GlobalDescr;
import org.drools.lang.descr.ImportDescr;
import org.drools.lang.descr.PackageDescr;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;

/**
 * Generic rule editor for drools.
 * @author Michael Neale
 */
public class DRLRuleEditor extends AbstractRuleEditor {

    protected List<String> imports;
    protected List<String> functions;
    protected Map<String, FactTemplateDescr> templates;
    protected List<GlobalDescr> globals;
    protected String packageName;
    protected List<String> classesInPackage;
	protected Map<String, String> attributes;

    public DRLRuleEditor() {
	}

	public List<String> getImports() {
		if (imports == null || isDirty()) {
			loadImportsAndFunctions();
		}
		return imports;
	}

	public List<String> getFunctions() {
		if (functions == null) {
			loadImportsAndFunctions();
		}
		return functions;
	}

	public Set<String> getTemplates() {
		if (templates == null) {
			loadImportsAndFunctions();
		}
		return templates.keySet();
	}

	public Map<String, String> getAttributes() {
		if ( attributes == null ) {
			loadImportsAndFunctions();
		}
		return attributes;
	}

	public FactTemplateDescr getTemplate(String name) {
		if (templates == null) {
			loadImportsAndFunctions();
		}
		return templates.get(name);
	}

	public List<GlobalDescr> getGlobals() {
		if (globals == null ) {
			loadImportsAndFunctions();
		}
		return globals;
	}

	public String getPackage() {
		if (packageName == null) {
			loadImportsAndFunctions();
		}
		return packageName;
	}

	public List<String> getClassesInPackage() {
		if (classesInPackage == null) {
			classesInPackage = getAllClassesInPackage(getPackage());
		}
		return classesInPackage;
	}

	protected List<String> getAllClassesInPackage(String packageName) {
		List<String> list = new ArrayList<String>();
		if (packageName != null) {
			IEditorInput input = getEditorInput();
			if (input instanceof IFileEditorInput) {
				IProject project = ((IFileEditorInput) input).getFile().getProject();
				IJavaProject javaProject = JavaCore.create(project);
				list = getAllClassesInPackage(packageName, javaProject);
			}
		}
		return list;
	}

	public static List<String> getAllClassesInPackage(String packageName, IJavaProject javaProject) {
		final List<String> list = new ArrayList<String>();
		CompletionRequestor requestor = new CompletionRequestor() {
			public void accept(org.eclipse.jdt.core.CompletionProposal proposal) {
				String className = new String(proposal.getCompletion());
				if (proposal.getKind() == org.eclipse.jdt.core.CompletionProposal.TYPE_REF) {
					list.add(className);
				}
				// ignore all other proposals
			}
		};

		try {
			javaProject.newEvaluationContext().codeComplete(packageName + ".", packageName.length() + 1, requestor);
		} catch (Throwable t) {
			DroolsEclipsePlugin.log(t);
		}
		return list;
	}

	protected List<String> getAllStaticMethodsInClass(String className) {
		final List<String> list = new ArrayList<String>();
		if (className != null) {
			IEditorInput input = getEditorInput();
			if (input instanceof IFileEditorInput) {
				IProject project = ((IFileEditorInput) input).getFile().getProject();
				IJavaProject javaProject = JavaCore.create(project);

				CompletionRequestor requestor = new CompletionRequestor() {
					public void accept(org.eclipse.jdt.core.CompletionProposal proposal) {
						String functionName = new String(proposal.getCompletion());
						if (proposal.getKind() == org.eclipse.jdt.core.CompletionProposal.METHOD_REF) {
							list.add(functionName.substring(0, functionName.length() - 2)); // remove the ()
						}
						// ignore all other proposals
					}
				};

				try {
					javaProject.newEvaluationContext().codeComplete(className + ".", className.length() + 1, requestor);
				} catch (Throwable t) {
					DroolsEclipsePlugin.log(t);
				}
			}
		}
		return list;
	}

    protected void loadImportsAndFunctions() {
        try {
            DRLInfo drlInfo = DroolsEclipsePlugin.getDefault().parseResource(this, true, false);
            PackageDescr descr = drlInfo.getPackageDescr();
            // package
            this.packageName = drlInfo.getPackageName();
            // imports
            List<ImportDescr> allImports = descr.getImports();
            this.imports = new ArrayList<String>();
            if (packageName != null) {
                imports.addAll(getAllClassesInPackage(packageName));
            }
            Iterator<ImportDescr> iterator = allImports.iterator();
            while (iterator.hasNext()) {
                String importName = iterator.next().getTarget();
                if (importName.endsWith(".*")) {
                    String packageName = importName.substring(0, importName.length() - 2);
                    imports.addAll(getAllClassesInPackage(packageName));
                } else {
                    imports.add(importName);
                }
            }
            // functions
            List<FunctionDescr> functionDescrs = descr.getFunctions();
            List<FunctionImportDescr> functionImports = descr.getFunctionImports();
            functions = new ArrayList<String>(functionDescrs.size());
            Iterator<FunctionDescr> iterator2 = functionDescrs.iterator();
            while (iterator2.hasNext()) {
                functions.add(iterator2.next().getName());
            }
            Iterator<FunctionImportDescr> iterator3 = functionImports.iterator();
            while (iterator3.hasNext()) {
                String functionImport = iterator3.next().getTarget();
                if (functionImport.endsWith(".*")) {
                    String className = functionImport.substring(0, functionImport.length() - 2);
                    functions.addAll(getAllStaticMethodsInClass(className));
                } else {
                    int index = functionImport.lastIndexOf('.');
                    if (index != -1) {
                        functions.add(functionImport.substring(index + 1));
                    }
                }
            }
            // templates
            List<FactTemplateDescr> templateDescrs = descr.getFactTemplates();
            templates = new HashMap<String, FactTemplateDescr>(templateDescrs.size());
            Iterator<FactTemplateDescr> iterator4 = templateDescrs.iterator();
            while (iterator4.hasNext()) {
                FactTemplateDescr template = iterator4.next();
                templates.put(template.getName(), template);
            }
            // globals
            List<GlobalDescr> globalDescrs = descr.getGlobals();
            globals = new ArrayList<GlobalDescr>();
            Iterator<GlobalDescr> iterator5 = globalDescrs.iterator();
            while (iterator5.hasNext()) {
                globals.add(iterator5.next());
            }

            //attributes
            this.attributes = new HashMap<String, String>();
        	for (Iterator<AttributeDescr> attrIter = descr.getAttributes().iterator(); attrIter.hasNext();) {
        		AttributeDescr attribute = attrIter.next();
        		if (attribute != null && attribute.getName() != null) {
        			attributes.put(attribute.getName(), attribute.getValue());
        		}
        	}

        } catch (DroolsParserException e) {
            DroolsEclipsePlugin.log(e);
        }
    }

	public Object getAdapter(Class adapter) {
		if (adapter.equals(IToggleBreakpointsTarget.class)) {
			return getBreakpointAdapter();
		}
		return super.getAdapter(adapter);
	}

	private Object getBreakpointAdapter() {
		return new DroolsLineBreakpointAdapter();
	}

	public void doSave(IProgressMonitor monitor) {
		super.doSave(monitor);
		// remove cached content
		imports = null;
		functions = null;
		templates = null;
		globals = null;
		packageName = null;
		classesInPackage = null;
	}

	public void gotoMarker(IMarker marker) {
		try {
			if (marker.getType().equals(IDroolsDebugConstants.DROOLS_MARKER_TYPE)) {
				int line = marker.getAttribute(IDroolsDebugConstants.DRL_LINE_NUMBER, -1);
	            if (line > -1)
	            	--line;
	                try {
	                    IDocument document = getDocumentProvider().getDocument(getEditorInput());
	                    selectAndReveal(document.getLineOffset(line), document.getLineLength(line));
	                } catch(BadLocationException exc) {
	                	DroolsEclipsePlugin.log(exc);
	                }
			} else {
				super.gotoMarker(marker);
			}
		} catch (CoreException exc) {
			DroolsEclipsePlugin.log(exc);
		}
	}


	public BaseDescr getDescr(int offset) {
		try {
			DRLInfo info = DroolsEclipsePlugin.getDefault().parseResource(this, true, false);
			return DescrUtil.getDescr(info.getPackageDescr(), offset);
		} catch (DroolsParserException exc) {
			return null;
		}
	}
}
