/******************************************************************************* 
 * Copyright (c) 2007 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.seam.internal.core.scanner.java;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.osgi.util.NLS;
import org.jboss.tools.common.model.util.EclipseJavaUtil;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.util.FileUtil;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.SeamCoreMessages;
import org.jboss.tools.seam.internal.core.scanner.IFileScanner;
import org.jboss.tools.seam.internal.core.scanner.LoadedDeclarations;
import org.jboss.tools.seam.internal.core.scanner.ScannerException;

/**
 * This object collects changes in target that should be fired to listeners.
 * 
 * @author Viacheslav Kabanovich
 */
public class JavaScanner implements IFileScanner {
	
	public JavaScanner() {}

	/**
	 * Returns true if file is java source - has *.java mask.
	 * @param resource
	 * @return
	 */	
	public boolean isRelevant(IFile resource) {
		if(resource.getName().endsWith(".java")) return true; //$NON-NLS-1$
		return false;
	}
	
	/**
	 * This method should be called only if isRelevant returns true;
	 * Makes simple check if this java file contains annotation Name. 
	 * @param resource
	 * @return
	 */
	public boolean isLikelyComponentSource(IFile f) {
		if(!f.isSynchronized(IFile.DEPTH_ZERO) || !f.exists()) return false;
		String content = FileUtil.readFile(f.getLocation().toFile());
		if(content == null) return false;
		int a = content.indexOf("org.jboss.seam.annotations."); //$NON-NLS-1$
		if(a < 0) {
			a = content.indexOf("javax.ejb.");
			if(a < 0) return false;
		}
		int i = content.indexOf("@"); //$NON-NLS-1$
		if(i < 0) return false;
		return true;
	}
	
	/**
	 * Returns component or list of component
	 * TODO change return type
	 * @param f
	 * @return
	 * @throws ScannerException
	 */
	public LoadedDeclarations parse(IFile f, ISeamProject sp) throws ScannerException {
		ICompilationUnit u = null;
		try {
			u = getCompilationUnit(f);
		} catch (CoreException e) {
			throw new ScannerException(
					NLS.bind(SeamCoreMessages.JAVA_SCANNER_CANNOT_GET_COMPILATION_UNIT_FOR,f), e);
		}
		if(u == null) return null;
		ICompilationUnit[] us = new ICompilationUnit[]{u};
		ASTParser p = ASTParser.newParser(AST.JLS3);
		p.setSource(u);
		p.setResolveBindings(true);
		if("package-info.java".equals(f.getFullPath().lastSegment())) {
			PackageInfoRequestor requestor = new PackageInfoRequestor(f);
			p.createASTs(us, new String[0], requestor, null);
			return requestor.getDeclarations();
		}
		ASTRequestorImpl requestor = new ASTRequestorImpl(f);
		p.createASTs(us, new String[0], requestor, null);
		return requestor.getDeclarations();
	}
	
	private ICompilationUnit getCompilationUnit(IFile f) throws CoreException {
		IProject project = f.getProject();
		IJavaProject javaProject = (IJavaProject)project.getNature(JavaCore.NATURE_ID);
		IResource[] rs = EclipseResourceUtil.getJavaSourceRoots(project);
		for (int i = 0; i < rs.length; i++) {
			if(rs[i].getFullPath().isPrefixOf(f.getFullPath())) {
				IPath path = f.getFullPath().removeFirstSegments(rs[i].getFullPath().segmentCount());
				IJavaElement e = javaProject.findElement(path);
				if(e == null && path.lastSegment().equals("package-info.java")) {
					//strange but sometimes only this works
					IJavaElement ep = javaProject.findElement(path.removeLastSegments(1));
					if(ep instanceof IPackageFragment) {
						e = ((IPackageFragment)ep).getCompilationUnit("package-info.java");
					}
				}
				if(e instanceof ICompilationUnit) {
					return (ICompilationUnit)e;
				}
			}
		}
		return null;
	}


	class ASTRequestorImpl extends ASTRequestor {
		private ASTVisitorImpl visitor = new ASTVisitorImpl();
		LoadedDeclarations ds = new LoadedDeclarations();
		IResource resource;
		IPath sourcePath;
		
		public ASTRequestorImpl(IResource resource) {
			this.resource = resource;
			this.sourcePath = resource.getFullPath();
		}

		public LoadedDeclarations getDeclarations() {
			return ds;
		}
		
		public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
			IType[] ts = null;
			try {
				ts = source.getTypes();
			} catch (JavaModelException e) {
				//ignore
			}
			if(ts == null || ts.length == 0) return;
			for (int i = 0; i < ts.length; i++) {
				visitor.setType(null);
				int f = 0;
				try {
					f = ts[i].getFlags();
				} catch (JavaModelException e) {
					//ignore
					continue;
				}
				if(Flags.isPublic(f)) {
					visitor.setType(ts[i]);
					ast.accept(visitor);
					if(!visitor.hasSeamComponent()) continue;
					processTypeData(visitor.root);
				}
			}
		}
		
		private void processTypeData(ASTVisitorImpl.TypeData data) {
			if(data.hasSeamComponentItself()) {
				ComponentBuilder b = new ComponentBuilder(ds, data);
				
				b.component.setSourcePath(sourcePath);
				b.component.setResource(resource);
				
				b.process();
			}
			for (ASTVisitorImpl.TypeData c: data.children) {
				processTypeData(c);
			}
		}
	}
	
	static String getResolvedType(IType type, String n) {
		String result = EclipseJavaUtil.resolveType(type, n);
		return result == null ? n : result;
	}

}
//:Pserver:anonymous@anoncvs.forge.jboss.com:/cvsroot/jboss