/*******************************************************************************
 * Copyright (c) 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.wtp.releng.tools.component.ui.internal;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
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.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.wtp.releng.tools.component.IClazz;
import org.eclipse.wtp.releng.tools.component.ILocation;
import org.eclipse.wtp.releng.tools.component.api.Source;
import org.eclipse.wtp.releng.tools.component.api.violation.ComponentViolationEmitter;
import org.eclipse.wtp.releng.tools.component.internal.Clazz;
import org.eclipse.wtp.releng.tools.component.internal.ComponentDepends;
import org.eclipse.wtp.releng.tools.component.internal.ComponentRef;
import org.eclipse.wtp.releng.tools.component.internal.ComponentXML;
import org.eclipse.wtp.releng.tools.component.ui.internal.WorkspaceFileLocation;
import org.eclipse.wtp.releng.tools.component.ui.internal.job.ScanComponent;

public class ScannableComponent implements IResourceProxyVisitor
{
  private ComponentXML compXML;
  private ComponentViolationEmitter emitter;
  private List projects;
  private long lastScan;
  private List newClasses;

  public ScannableComponent(ComponentXML compXML, ComponentViolationEmitter emitter, List projects)
  {
    this.compXML = compXML;
    this.emitter = emitter;
    this.projects = projects;
    this.lastScan = -1;
    this.newClasses = new ArrayList();
  }

  /**
   * @return Returns the compXML.
   */
  public ComponentXML getCompXML()
  {
    return compXML;
  }

  /**
   * @return Returns the emitter.
   */
  public ComponentViolationEmitter getEmitter()
  {
    return emitter;
  }

  /**
   * @return Returns the projects.
   */
  public List getProjects()
  {
    if (projects == null)
      projects = new ArrayList(1);
    return projects;
  }

  public void addProject(IProject project)
  {
    getProjects().add(project);
    new ScanComponent(this, true).schedule();
  }

  public IProject removeProject(String projectName)
  {
    if (projects != null)
      for (int i = 0; i < projects.size(); i++)
        if (((IProject)projects.get(i)).getName().equals(projectName))
          return (IProject)projects.remove(i);
    return null;
  }

  public void addCompRef(ComponentXML compRef)
  {
    Map compRefs = emitter.getCompRefs();
    compRefs.put(compRef.getLocation().getAbsolutePath(), compRef);
    emitter.setCompRefs(compRefs);
    if (isDependsOn(compRef.getName()))
      new ScanComponent(this, true).schedule();
  }

  public void removeCompRef(ILocation location)
  {
    Map compRefs = emitter.getCompRefs();
    String key = location.getAbsolutePath();
    if (compRefs.containsKey(key))
    {
      ComponentXML compRef = (ComponentXML)compRefs.remove(key);
      emitter.setCompRefs(compRefs);
      if (isDependsOn(compRef.getName()))
      {
        new ScanComponent(this, true).schedule();
      }
    }
  }

  private boolean isDependsOn(String compRefName)
  {
    if (!compRefName.equals(compXML.getName()))
    {
      ComponentDepends depends = compXML.getComponentDepends();
      if (depends.isUnrestricted())
        return true;
      else
      {
        Collection compRefs = depends.getComponentRefs();
        for (Iterator it = compRefs.iterator(); it.hasNext();)
          if (((ComponentRef)it.next()).getName().equals(compRefName))
            return true;
      }
    }
    return false;
  }

  public boolean isScanningProject(IProject project)
  {
    if (projects != null && project != null)
      for (Iterator it = projects.iterator(); it.hasNext();)
        if (project.getName().equals(((IProject)it.next()).getName()))
          return true;
    return false;
  }

  public List scan() throws IOException
  {
    return scan(false);
  }

  public synchronized List scan(boolean force) throws IOException
  {
    if (force)
      lastScan = -1;
    newClasses.clear();
    List sources = new ArrayList();
    IJobManager jobManager = Platform.getJobManager();
    for (int i = 0; i < projects.size(); i++)
    {
      IProject project = (IProject)projects.get(i);
      try
      {
        jobManager.beginRule(project, new NullProgressMonitor());
      }
      finally
      {
        jobManager.endRule(project);
      }
      try
      {
        project.accept(this, IResource.DEPTH_INFINITE | IResource.NONE);
      }
      catch (CoreException e)
      {
        // should never happen
        e.printStackTrace();
      }
    }
    lastScan = Calendar.getInstance().getTimeInMillis();
    String compLoc = compXML.getLocation().getAbsolutePath();
    for (Iterator it = newClasses.iterator(); it.hasNext();)
    {
      Source source = emitter.genViolation(compLoc, (IClazz)it.next());
      if (source != null)
        sources.add(source);
    }
    newClasses.clear();
    return sources;
  }

  public boolean visit(IResourceProxy resProxy)
  {
    if (resProxy.getType() == IResource.FILE && resProxy.getName().endsWith(".class") && resProxy.getModificationStamp() > lastScan)
      newClasses.add(new Clazz(new WorkspaceFileLocation((IFile)resProxy.requestResource())));
    return true;
  }
}