package org.eclipse.stp.bpmn.validation.builder;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.gmf.runtime.emf.core.resources.GMFResourceFactory;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.stp.bpmn.BpmnPackage;
import org.eclipse.stp.bpmn.validation.BpmnValidationPlugin;
import org.eclipse.stp.bpmn.validation.providers.BpmnValidationProvider;

public class BatchValidationBuilder extends IncrementalProjectBuilder {

	class BpmnDiagramResourceDeltaVisitor implements IResourceDeltaVisitor {
        
        IProgressMonitor _monitor;
        BpmnDiagramResourceDeltaVisitor(IProgressMonitor monitor) {
            _monitor = monitor;
        }
		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
		 */
		public boolean visit(IResourceDelta delta) throws CoreException {
			IResource resource = delta.getResource();
			switch (delta.getKind()) {
			case IResourceDelta.ADDED:
				// handle added resource
				checkBpmnResource(resource, _monitor);
				break;
			case IResourceDelta.REMOVED:
				// handle removed resource
				break;
			case IResourceDelta.CHANGED:
				// handle changed resource
                checkBpmnResource(resource, _monitor);
				break;
			}
			//return true to continue visiting children.
			return true;
		}
	}

	class BpmnDiagramResourceVisitor implements IResourceVisitor {
        
        IProgressMonitor _monitor;
        BpmnDiagramResourceVisitor(IProgressMonitor monitor) {
            _monitor = monitor;
        }
        
        public boolean visit(IResource resource) {
            checkBpmnResource(resource, _monitor);
			//return true to continue visiting children.
			return true;
		}
	}

	public static final String BUILDER_ID = "org.eclipse.stp.bpmn.validation.BatchValidationBuilder";

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.core.internal.events.InternalBuilder#build(int,
	 *      java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
	 */
	protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
			throws CoreException {
		if (kind == FULL_BUILD) {
			fullBuild(monitor);
		} else {
			IResourceDelta delta = getDelta(getProject());
			if (delta == null) {
				fullBuild(monitor);
			} else {
				incrementalBuild(delta, monitor);
			}
		}
		return null;
	}
    
    private static GMFResourceFactory RESOURCE_FACTORY = new GMFResourceFactory();

	void checkBpmnResource(IResource resource, IProgressMonitor monitor) {
		if (resource instanceof IFile && resource.getName().endsWith(".bpmn_diagram")) {
			IFile file = (IFile) resource;
            try {
                file.deleteMarkers(BpmnValidationProvider.MARKER_TYPE, 
                        false, IResource.DEPTH_ZERO);
            } catch (CoreException ce) {
            }
            try {
                validateDiagramFile(file, monitor);
            } catch (IOException e) {
                e.printStackTrace();
            }
		}
	}

	protected void fullBuild(final IProgressMonitor monitor)
			throws CoreException {
		try {
			getProject().accept(new BpmnDiagramResourceVisitor(monitor));
		} catch (CoreException e) {
		}
	}

	protected void incrementalBuild(IResourceDelta delta,
			IProgressMonitor monitor) throws CoreException {
		delta.accept(new BpmnDiagramResourceDeltaVisitor(monitor));
	}
    
    
    /**
     * Calls the batch validation on a diagram file.
     * Used by builders.
     * 
     * @param diagramFile
     * @param monitor
     * @throws IOException
     */
    public static final void validateDiagramFile(IFile diagramFile,
            IProgressMonitor monitor) throws IOException {
        ResourceSet resourceSet = new ResourceSetImpl();
        resourceSet.getResourceFactoryRegistry()
            .getExtensionToFactoryMap().put("bpmn", RESOURCE_FACTORY);
        resourceSet.getResourceFactoryRegistry()
            .getExtensionToFactoryMap().put("bpmn_diagram", RESOURCE_FACTORY);
        resourceSet.getPackageRegistry().put(NotationPackage.eNS_URI,
                NotationPackage.eINSTANCE);
        resourceSet.getPackageRegistry().put(BpmnPackage.eNS_URI,
                BpmnPackage.eINSTANCE);
        
        //make sure we can load the bpmnResource:
        IPath bpmnPath =
            diagramFile.getFullPath()
                .removeFileExtension().addFileExtension("bpmn");
        
        if (ResourcesPlugin.getWorkspace().getRoot().findMember(bpmnPath) == null) {
            BpmnValidationPlugin.getDefault().getLog().log(
                    new Status(IStatus.WARNING,
                            BpmnValidationPlugin.PLUGIN_ID,
                            IStatus.WARNING,
                            "The diagram " +
                            diagramFile.getFullPath().toPortableString() +
                            " has no corresponding model: " + bpmnPath, null));
            return;
        }
        
        URI uriBpmn = URI.createURI("platform:/resource" +
                diagramFile.getFullPath().removeFileExtension().toPortableString()
                + ".bpmn");
        
        URI uriBpmnDiagram = URI.createURI("platform:/resource" +
                diagramFile.getFullPath().toPortableString());
        
        Resource resBpmn = resourceSet.createResource(uriBpmn);
        Resource resBpmnDiag = resourceSet.createResource(uriBpmnDiagram);
        
        resBpmnDiag.load(RESOURCE_FACTORY.getDefaultLoadOptions());
        resBpmn.load(RESOURCE_FACTORY.getDefaultLoadOptions());
        
        Collection<EObject> selectedEObjects = resBpmnDiag.getContents();
        if (selectedEObjects.isEmpty()) {
            BpmnValidationPlugin.getDefault().getLog().log(
                    new Status(IStatus.WARNING,
                            BpmnValidationPlugin.PLUGIN_ID,
                            IStatus.WARNING,
                            "The diagram " +
                            diagramFile.getFullPath().toPortableString() +
                            " was empty.", null));
            return;
        }
        monitor.setTaskName("Validate diagram " + 
                diagramFile.getFullPath().toPortableString());
        for (EObject eobj : selectedEObjects) {
            if (eobj instanceof Diagram) {
                final Diagram diagram = (Diagram)eobj;
                try {
                    BpmnValidationProvider.ValidateAction.runValidation(diagram);
                } catch (Exception e) {
                    BpmnValidationPlugin.getDefault().getLog().log(
                            new Status(IStatus.ERROR,
                                    BpmnValidationPlugin.PLUGIN_ID,
                                    IStatus.ERROR,
                                    "Validation action failed", e));
                }
            } else {
                
            }
        }
        monitor.done();
        
    }

}
