/*
 * VTaskImpl.java
 *
 * Created on July 24, 2003, 5:08 PM
 */

package org.jboss.tools.common.verification.vrules.impl;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.jboss.tools.common.verification.vrules.VAction;
import org.jboss.tools.common.verification.vrules.VEntity;
import org.jboss.tools.common.verification.vrules.VHelper;
import org.jboss.tools.common.verification.vrules.VManager;
import org.jboss.tools.common.verification.vrules.VObject;
import org.jboss.tools.common.verification.vrules.VResult;
import org.jboss.tools.common.verification.vrules.VRule;
import org.jboss.tools.common.verification.vrules.VTask;
import org.jboss.tools.common.verification.vrules.VTaskListener;
import org.jboss.tools.common.verification.vrules.plugin.VerificationPlugin;

/**
 *
 * @author  valera
 */
public class VTaskImpl implements VTask, Runnable {
    
    private VObject object;
    private VRule[] rules;
    private int index = 0;
    private List<VTaskListener> listeners = new ArrayList<VTaskListener>();
    private Thread thread;
    private boolean running = false;
    private boolean sleeping = false;

    /** Creates a new instance of VTaskImpl */
    public VTaskImpl(VManager manager, VObject object) {
        this.object = object;
        this.rules = VHelper.getRules(manager, object);
    }

    public VTaskImpl(VObject object, VRule[] rules) {
        this.object = object;
        this.rules = rules;
    }

    public void start() {
        if (thread == null) {
            thread = new Thread(this, "VTask"); //$NON-NLS-1$
            thread.start();
        } else {
            if(sleeping) {
                synchronized(this) {
                    sleeping = false;
                    try {
                    	notifyAll();
                    } catch (IllegalMonitorStateException e) {
                    	//ignore
                    }
                }
            }
        }
    }

    public boolean isSleeping() {
        return sleeping;
    }

    public boolean isRunning() {
        return running;
    }

    public synchronized void pause() {
        if(running && !sleeping) sleeping = true;
    }

    public void stop() {
        sleeping = false;
        running = false;
    }

    public void run() {
        notifyStarted();
        if(rules != null) for (index = 0; index < rules.length; index++) {
            if (!running) break;
            VRule rule = rules[index];
            if (rule.isEnabled() && rule.getAction() != null
                && rule.getRuleSet().isEnabled()) {
                apply(rule, object);
            }
            notifyRuleFinished(rule, object);
        }
        notifyFinish();
    }

    private void checkSleeping() {
        if(sleeping) {
            synchronized(this) {
                if(sleeping) {
                    notifyPause();
                    try {
                    	wait();
                    } catch (InterruptedException e) {
                    	//ignore
                    }
                    if(!sleeping) notifyResume();
                }
            }
        }
    }

    private void apply(VRule rule, VObject object) {
        VEntity entity = object.getEntity();
        VEntity[] entities = rule.getEntities();
        boolean checkChildren = false;
        boolean checked = false;
        for (int i = 0; i < entities.length; i++) {
            if(!running) return;
            checkSleeping();
            if(!running) return;
            if (entity != null && entities[i] != null && entity.getName().equals(entities[i].getName())) {
                VAction action = rule.getAction();
                if (action == null) {
                	continue;// should not happen
                }
                VResult[] results = action.check(object);
                notifyApplied(rule, object, results);
                checked = true;
                if (checkChildren) {
                	break;
                }
            } else if (entity.isDescendant(entities[i].getName())) {
                checkChildren = true;
                if (checked) {
                	break;
                }
            }
        }
        if (checkChildren) {
            VObject[] children = object.getChildren();
            for (int i = 0; i < children.length; i++) {
                apply(rule, children[i]);
            }
        }
    }

    private void notifyStarted() {
        running = true;
        sleeping = false;
        synchronized (listeners) {
            for (int i = 0; i < listeners.size(); i++) {
                SafeRunner.run(new OnStartNotifier((VTaskListener)listeners.get(i)));
            }
        }
    }
    
    private void logListener(VTaskListener listener, Throwable e) { 
		VerificationPlugin.getPluginLog().logInfo("Exception in listener "+listener+" ignored: "+e, e); //$NON-NLS-1$ //$NON-NLS-2$
    }

    private void notifyApplied(VRule rule, VObject object, VResult[] results) {
        synchronized (listeners) {
            for (int i = 0; i < listeners.size(); i++) {
               SafeRunner.run(new OnRuleAppliedNotifier((VTaskListener)listeners.get(i),rule, object, results));
            }
        }
    }

    private void notifyRuleFinished(VRule rule, VObject object) {
		if(!running) return;
        synchronized (listeners) {
            for (int i = 0; i < listeners.size(); i++) {
               SafeRunner.run(new OnRuleFinishedNotifier((VTaskListener)listeners.get(i),rule, object));
            }
        }
    }

    private void notifyPause() {
    	if(!running) return;
        synchronized (listeners) {
            for (int i = 0; i < listeners.size(); i++) {
                SafeRunner.run(new OnPauseNotifier((VTaskListener)listeners.get(i)));
            }
        }
    }

    private void notifyResume() {
		if(!running) return;
        synchronized (listeners) {
            for (int i = 0; i < listeners.size(); i++) {
                SafeRunner.run(new OnResumeNotifier((VTaskListener)listeners.get(i)));
            }
        }
    }

    private void notifyFinish() {
        sleeping = false;
        running = false;
        thread = null;
		VTaskListener[] ls = null; 
        synchronized (listeners) {
        	ls = (VTaskListener[])listeners.toArray(new VTaskListener[0]);
        }
        for (int i = 0; i < ls.length; i++) {
            SafeRunner.run(new OnFinishNotifier(ls[i]));
        }
    }

    public void addTaskListener(VTaskListener listener) {
        synchronized (listeners) {
            listeners.add(listener);
        }
    }
    
    public void removeTaskListener(VTaskListener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }
    }
    
    public class SafeNotifier implements ISafeRunnable {
    	
		protected VTaskListener listener;
		
		public SafeNotifier(VTaskListener listener) {
			this.listener = listener;
		}
		public void run() throws Exception {
		       listener.onStart();
		}
		public void handleException(Throwable exception) {
			logListener(listener, exception);
		}
    }
    
    public class OnStartNotifier extends SafeNotifier {
		public OnStartNotifier(VTaskListener listener) {
			super(listener);
		}
		public void run() throws Exception {
		       listener.onStart();
		}
    }
    
    public class OnPauseNotifier extends SafeNotifier {
		public OnPauseNotifier(VTaskListener listener) {
			super(listener);
		}
		public void run() throws Exception {
		       listener.onPause();
		}
    }
    
    public class OnResumeNotifier extends SafeNotifier {
		public OnResumeNotifier(VTaskListener listener) {
			super(listener);
		}
		public void run() throws Exception {
		       listener.onResume();
		}
    }
    
    public class OnFinishNotifier extends SafeNotifier {
		public OnFinishNotifier(VTaskListener listener) {
			super(listener);
		}
		public void run() throws Exception {
		       listener.onFinish();
		}
    }
    
    public class OnRuleFinishedNotifier extends SafeNotifier {
    	VRule rule;
    	VObject object; 
		public OnRuleFinishedNotifier(VTaskListener listener,VRule rule, VObject object) {
			super(listener);
			this.rule = rule;
			this.object = object;
		}
		public void run() throws Exception {
		       listener.onRuleFinished(rule, object);
		}
    }
    
    public class OnRuleAppliedNotifier extends OnRuleFinishedNotifier {
    	VResult[] results;
		public OnRuleAppliedNotifier(VTaskListener listener,VRule rule, VObject object, VResult[] results) {
			super(listener,rule,object);
			this.results =results;
		}
		public void run() throws Exception {
		       listener.onRuleApplied(rule, object, results);
		}
    }
    

}
