/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and 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:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.project;

import java.util.*;

import org.jboss.tools.common.model.markers.ResourceMarkers;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.model.event.*;
import org.jboss.tools.common.model.filesystems.FileSystemsHelper;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.model.util.ModelFeatureFactory;

public class Watcher implements XModelTreeListener {
    public static Watcher getInstance(XModel model) {
		Watcher instance = (Watcher)model.getManager("Watcher"); //$NON-NLS-1$
        if(instance == null) {
			instance = new Watcher();
            instance.setModel(model);
			model.addManager("Watcher", instance); //$NON-NLS-1$
            model.addModelTreeListener(instance);
        }
        return instance;
    }

    protected XModel model;
    protected Map<String,IWatcherContributor> contributors = new HashMap<String, IWatcherContributor>();
    private boolean lock = false;

	class WatcherRunnable implements XJob.XRunnable {
		String id = "Watcher - " + XModelConstants.getWorkspace(model); //$NON-NLS-1$

		public String getId() {
			return id;
		}

		public void run() {
			Watcher.this.updateAll();
		}
		
	}
	
    private Watcher() {
    }
    
    void updateContributors() {
    	if(model == null) return;
    	ModelNatureExtension[] es = ModelNatureExtension.getInstances();
    	for (int i = 0; i < es.length; i++) {
    		String nature = es[i].getName();
    		if(EclipseResourceUtil.hasNature(model, nature)) {
    			if(contributors.containsKey(nature)) {
    				continue;
    			} else {
    				String contributorName = es[i].getWatcherContributor();
   	    			Object watcher = ModelFeatureFactory.getInstance().createFeatureInstance(contributorName);
   	    			if(watcher instanceof IWatcherContributor) {
   	    				IWatcherContributor c = (IWatcherContributor)watcher;
   	    				c.init(model);
   	    				contributors.put(nature, c);
   	    			} else if(ModelPlugin.isDebugEnabled()) {			
   						ModelPlugin.getPluginLog().logInfo("Class is not implemented IWatcherContributor interface!"); //$NON-NLS-1$
  					}
    			}
    		} else {
    			contributors.remove(nature);
    		}
    	}
    }

    public void setModel(XModel model) {
        this.model = model;
    }
    
    public void forceUpdate() {
    	if(model.getProperties().getProperty(IModelNature.ECLIPSE_PROJECT) == null) return;
    	XJob.addRunnable(new WatcherRunnable());
    }

    private void updateAll() {
        if(lock) return;
        lock();
        updateContributors();
        try {
        	String err = null;
        	for (IWatcherContributor c : contributors.values()) {
        		if(!c.isActive()) continue;
        		c.update();
				if(err == null) {
					err = c.getError();
				}
        	}
        	setError(err);
            setCorrect(err == null);
        	for (IWatcherContributor c : contributors.values()) {
        		if(!c.isActive()) continue;
				c.updateProject();
			}
        } finally {
            unlock();
        }
    }

    public void lock() {
        lock = true;
    }

    public void unlock() {
        lock = false;
    }

    public void structureChanged(XModelTreeEvent event) {
        XModel model = event.getModelObject().getModel();
        if (event.kind() == XModelTreeEvent.STRUCTURE_CHANGED &&
                event.getModelObject() == model.getRoot()) {
            model.removeModelTreeListener(this);
///            stopped = true;
            model.removeManager("Watcher"); //$NON-NLS-1$
            return;
        }
        forceUpdate();
    }
    
    public void nodeChanged(XModelTreeEvent event) {
    	forceUpdate();
    }

    
	String error = "initial"; //$NON-NLS-1$
	ResourceMarkers markers = null;

	class RM extends ResourceMarkers {
		public RM() {
			super(ResourceMarkers.JST_WEB_PROBLEM, ResourceMarkers.OLD_JST_WEB_PROBLEM);
		}
		protected String[] getErrors() {
			return (error == null) ? new String[0] : new String[]{error};
		}
	}

	public void setError(String err) {
		if(markers == null) {
			markers = new RM();
			markers.setModelObject(FileSystemsHelper.getFileSystems(model));
			markers.clear();
		}
		boolean changed = (error == null) ? (err != null) : !error.equals(err);
		if(changed) {
			error = err;
			markers.update();
		}
	}

	private void setCorrect(boolean correct) {
		XModelObject fs = FileSystemsHelper.getFileSystems(model);
		if(fs == null) return;
		boolean b = !XModelObjectConstants.YES.equals(fs.get("_hasErrors_")); //$NON-NLS-1$
		if(b == correct) return;
		fs.set("_hasErrors_", (correct) ? "" : XModelObjectConstants.YES); //$NON-NLS-1$ //$NON-NLS-2$
		fs.fireObjectChanged("_hasErrors_"); //$NON-NLS-1$
	}

}
