/******************************************************************************* 
 * Copyright (c) 2010 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.ws.creation.core.commands;

import java.io.File;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.wst.common.frameworks.datamodel.AbstractDataModelOperation;
import org.jboss.tools.ws.core.utils.StatusUtils;
import org.jboss.tools.ws.creation.core.JBossWSCreationCorePlugin;
import org.jboss.tools.ws.creation.core.data.ServiceModel;
import org.jboss.tools.ws.creation.core.messages.JBossWSCreationCoreMessages;
import org.jboss.tools.ws.creation.core.utils.JBossWSCreationUtils;

/**
 * @author Brian Fitzpatrick
 *
 */
public class RSServiceCreationCommand extends AbstractDataModelOperation {

	private ServiceModel model;
	private IResource annotatedClassResource;
	private IResource applicationClassResource;
	private static String JAVA = ".java"; //$NON-NLS-1$

	public static final String LINE_SEPARATOR = System
			.getProperty("line.separator"); //$NON-NLS-1$

	public RSServiceCreationCommand(ServiceModel model) {
		this.model = model;
	}

	public IResource getAnnotatedClassResource() {
		return this.annotatedClassResource;
	}
	
	public IResource getApplicationClassResource() {
		return this.applicationClassResource;
	}

	@Override
	public IStatus execute(IProgressMonitor monitor, IAdaptable info)
			throws ExecutionException {
		IJavaProject project = null;
		try {
			project = JBossWSCreationUtils.getJavaProjectByName(model
					.getWebProjectName());
		} catch (JavaModelException e) {
			JBossWSCreationCorePlugin.getDefault().logError(e);
			return StatusUtils
					.errorStatus(JBossWSCreationCoreMessages.Error_Create_Client_Sample);
		}

		// find the class, make sure it's in the project and open if we find it
		if (model.getServiceClasses() != null && model.getServiceClasses().size() == 1) {
			String clazzName = model.getServiceClasses().get(0);
			File file = JBossWSCreationUtils.findFileByPath(clazzName + JAVA, project.getProject()
					.getLocation().toOSString());
			if (file == null) {
				ICompilationUnit rsAnnotatedClass =
					createRESTAnnotatedJavaClass (model.getCustomPackage(), JBossWSCreationUtils
						.classNameFromQualifiedName(model.getServiceClasses().get(0)),
						project);
				if (rsAnnotatedClass != null) {
					this.annotatedClassResource = rsAnnotatedClass.getResource();
				}
			} else {
				IResource test = findClass(project, clazzName);
				if (test != null) {
					this.annotatedClassResource = test;
				}
			}
		}
		
		File file = JBossWSCreationUtils.findFileByPath(model.getApplicationClassName() + JAVA, project.getProject()
				.getLocation().toOSString());

		if (file == null) {
			ICompilationUnit appClass = createRESTApplicationClass (model.getCustomPackage(), model.getApplicationClassName(),
					project);
			if (appClass != null) {
				this.applicationClassResource = appClass.getResource();
			} else {
				IResource test = findClass(project, model.getApplicationClassName());
				if (test != null) {
					this.applicationClassResource = test;
				}
			}
		}

		return null;
	}

	public IResource findClass(IJavaProject project, String className) {
		try {
			IType type = project.findType(className);
			if (type != null) {
				final IPackageFragment fragment = type.getPackageFragment();
				return fragment.getResource();
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return null;
	}

	private ICompilationUnit createRESTApplicationClass(String packageName,
			String className, IJavaProject project) {
		try {
			IPath srcPath = new Path(JBossWSCreationUtils
					.getJavaProjectSrcLocation(project.getProject()));
			srcPath = project.getPath().append(
					srcPath.makeRelativeTo(project.getProject()
							.getLocation()));
			IPackageFragmentRoot root = project
					.findPackageFragmentRoot(srcPath);
			if (packageName == null) {
				packageName = ""; //$NON-NLS-1$
			}
			IPackageFragment pkg = root.createPackageFragment(packageName,
					false, null);
			ICompilationUnit wrapperCls = pkg.createCompilationUnit(className
					+ ".java", "", true, null); //$NON-NLS-1$//$NON-NLS-2$
			if (!packageName.equals("")) { //$NON-NLS-1$
				wrapperCls.createPackageDeclaration(packageName, null);
			}

			StringBuffer clsContent = new StringBuffer(); 
			clsContent.append("public class ").append(className).append(" extends Application").append(" {" + LINE_SEPARATOR); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			clsContent.append("}").append(LINE_SEPARATOR); //$NON-NLS-1$
			wrapperCls.createType(clsContent.toString(), null, true, null);
			
			wrapperCls.createImport("java.util.Set", null,null); //$NON-NLS-1$
			wrapperCls.createImport("java.util.HashSet", null,null); //$NON-NLS-1$
			wrapperCls.createImport("javax.ws.rs.core.Application", null,null); //$NON-NLS-1$
			
			IType serviceClsType = wrapperCls.findPrimaryType();
			clsContent = new StringBuffer();
			clsContent.append("private Set<Object> singletons = new HashSet<Object>();" + LINE_SEPARATOR); //$NON-NLS-1$
			serviceClsType.createField(clsContent.toString(), null, false, null);
			
			clsContent = new StringBuffer();
			clsContent.append("private Set<Class<?>> empty = new HashSet<Class<?>>();" + LINE_SEPARATOR); //$NON-NLS-1$
			serviceClsType.createField(clsContent.toString(), null, false, null);

			clsContent = new StringBuffer();
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("public " + className + "(){" + LINE_SEPARATOR); //$NON-NLS-1$ //$NON-NLS-2$
			clsContent.append("     singletons.add(new " + JBossWSCreationUtils //$NON-NLS-1$
					.classNameFromQualifiedName(model.getServiceClasses().get(0)) + "());" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("}" + LINE_SEPARATOR); //$NON-NLS-1$
			serviceClsType.createMethod(clsContent.toString(), null, true, null);

			clsContent = new StringBuffer();
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("@Override" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("public Set<Class<?>> getClasses() {" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("     return empty;" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("}" + LINE_SEPARATOR); //$NON-NLS-1$
			serviceClsType.createMethod(clsContent.toString(), null, true, null);

			clsContent = new StringBuffer();
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("@Override" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("public Set<Object> getSingletons() {" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("     return singletons;" + LINE_SEPARATOR); //$NON-NLS-1$
			clsContent.append("}" + LINE_SEPARATOR); //$NON-NLS-1$
			serviceClsType.createMethod(clsContent.toString(), null, true, null);

			wrapperCls.save(null, true);
			return wrapperCls;
		} catch (Exception e) {
			JBossWSCreationCorePlugin.getDefault().logError(e);
			return null;
		}
	}

	private ICompilationUnit createRESTAnnotatedJavaClass(String packageName,
			String className, IJavaProject project) {
		try {
			IPath srcPath = new Path(JBossWSCreationUtils
					.getJavaProjectSrcLocation(project.getProject()));
			srcPath = project.getPath().append(
					srcPath.makeRelativeTo(project.getProject()
							.getLocation()));
			IPackageFragmentRoot root = project
					.findPackageFragmentRoot(srcPath);
			if (packageName == null) {
				packageName = ""; //$NON-NLS-1$
			}
			IPackageFragment pkg = root.createPackageFragment(packageName,
					false, null);
			ICompilationUnit wrapperCls = pkg.createCompilationUnit(className
					+ ".java", "", true, null); //$NON-NLS-1$//$NON-NLS-2$
			if (!packageName.equals("")) { //$NON-NLS-1$
				wrapperCls.createPackageDeclaration(packageName, null);
			}

			StringBuffer clsContent = new StringBuffer(); 
			clsContent.append("@Path(\"/" + model.getServiceName() + "\")").append(LINE_SEPARATOR); //$NON-NLS-1$ //$NON-NLS-2$
			clsContent.append("public class ").append(className).append(" {" + LINE_SEPARATOR); //$NON-NLS-1$ //$NON-NLS-2$
			clsContent.append("}").append(LINE_SEPARATOR); //$NON-NLS-1$
			wrapperCls.createType(clsContent.toString(), null, true, null);
			
			wrapperCls.createImport("javax.ws.rs.Produces", null,null); //$NON-NLS-1$
			wrapperCls.createImport("javax.ws.rs.GET", null,null); //$NON-NLS-1$
			wrapperCls.createImport("javax.ws.rs.Path", null,null); //$NON-NLS-1$
			
			IType serviceClsType = wrapperCls.findPrimaryType();
			clsContent = new StringBuffer();
			clsContent.append("@GET()"); //$NON-NLS-1$
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("@Produces(\"text/plain\")"); //$NON-NLS-1$
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("public String sayHello() {"); //$NON-NLS-1$
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("    return \"Hello World!\";"); //$NON-NLS-1$
			clsContent.append(LINE_SEPARATOR);
			clsContent.append("}"); //$NON-NLS-1$
			serviceClsType.createMethod(clsContent.toString(), null, true, null);
			wrapperCls.save(null, true);
			return wrapperCls;
		} catch (Exception e) {
			JBossWSCreationCorePlugin.getDefault().logError(e);
			return null;
		}
	}
}
