

package com.laytonsmith.core.events;

import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery;
import com.laytonsmith.annotations.hide;
import com.laytonsmith.core.LogLevel;
import com.laytonsmith.core.MethodScriptCompiler;
import com.laytonsmith.core.ParseTree;
import com.laytonsmith.core.PermissionsResolver;
import com.laytonsmith.core.Script;
import com.laytonsmith.core.constructs.CArray;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.exceptions.CancelCommandException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import com.laytonsmith.core.exceptions.EventException;
import com.laytonsmith.core.exceptions.ProgramFlowManipulationException;
import com.laytonsmith.core.functions.Exceptions;
import com.laytonsmith.core.profiler.ProfilePoint;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * This helper class implements a few of the common functions in event, and
 * most (all?) Events should extend this class.
 * @author layton
 */
public abstract class AbstractEvent implements Event, Comparable<Event> {
    
    private EventMixinInterface mixin;
//    protected EventHandlerInterface handler;
//    
//    protected AbstractEvent(EventHandlerInterface handler){
//        this.handler = handler;
//    }
//    
    public final void setAbstractEventMixin(EventMixinInterface mixin){
        this.mixin = mixin;
    }
    

    /**
     * If the event needs to run special code when a player binds the event, it
     * can be done here. By default, an UnsupportedOperationException is thrown,
     * but is caught and ignored.
     */
    public void bind(BoundEvent event) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * If the event needs to run special code at server startup, it can be done
     * here. By default, nothing happens.
     */
    public void hook() {
        
    }

    
    /**
     * This function is run when the actual event occurs.
	 * @param tree The compiled parse tree
     * @param b The bound event
	 * @param env The operating environment
	 * @param activeEvent The active event being executed
     */
    public final void execute(ParseTree tree, BoundEvent b, Environment env, BoundEvent.ActiveEvent activeEvent) throws ConfigRuntimeException{          
        try{
            preExecution(env, activeEvent);
        } catch(UnsupportedOperationException e){
            //Ignore. This particular event doesn't need to customize
        }
		ProfilePoint event = null;
		if(env.getEnv(GlobalEnv.class).GetProfiler() != null){
			event = env.getEnv(GlobalEnv.class).GetProfiler().start("Event " + b.getEventName() + " (defined at " + b.getTarget().toString() + ")", LogLevel.ERROR);
		}
		try {
			try {
				//Get the label from the bind time environment, and put it in the current
				//environment.
				String label = b.getEnvironment().getEnv(GlobalEnv.class).GetLabel();
				env.getEnv(GlobalEnv.class).SetLabel(label);
				MethodScriptCompiler.execute(tree, env, null, null);
			} catch(CancelCommandException ex){
				if(ex.getMessage() != null && !ex.getMessage().equals("")){
					System.out.println(ex.getMessage());
				}
			} catch(ProgramFlowManipulationException ex){
				ConfigRuntimeException.React(new ConfigRuntimeException("", Exceptions.ExceptionType.FormatException, ex.getTarget()), env);
			}
		} finally {
			if(event != null){
				event.stop();
			}
		}
        try{
            this.postExecution(env, activeEvent);
        } catch(UnsupportedOperationException e){
            //Ignore.
        }
    }
    
	/**
	 * This method is called before the event handling code is run, and provides a place
	 * for the event code itself to modify the environment or active event data.
	 * @param env The environment, at the time just before the event handler is called.
	 * @param activeEvent The event handler code.
	 * @throws UnsupportedOperationException If the preExecution isn't supported, this may
	 * be thrown, and it will be ignored.
	 */
    public void preExecution(Environment env, BoundEvent.ActiveEvent activeEvent){
        
    }
    
	/**
	 * This method is called after the event handling code is run, and provides a place
	 * for the event code itself to modify or cleanup the environment or active event data.
	 * @param env The environment, at the time just before the event handler is called.
	 * @param activeEvent The event handler code.
	 * @throws UnsupportedOperationException If the preExecution isn't supported, this may
	 * be thrown, and it will be ignored.
	 */
    public void postExecution(Environment env, BoundEvent.ActiveEvent activeEvent){
        
    }

    /**
     * For sorting and optimizing events, we need a comparison operation. By default
     * it is compared by looking at the event name.
     * @param o
     * @return 
     */
    public int compareTo(Event o) {
        return this.getName().compareTo(o.getName());
    }
    
    /**
     * Since most events are minecraft events, we return true by default.
     * @return 
     */
	@Override
    public boolean supportsExternal(){
        return true;
    }
    
    /**
     * If it is ok to by default do a simple conversion from a CArray to a
     * Map, this method can do it for you. Likely this is not acceptable,
     * so hard-coding the conversion will be necessary.
     * @param manualObject
     * @return 
     */
    public static Object DoConvert(CArray manualObject){
        Map<String, Construct> map = new HashMap<String, Construct>();
        for(String key : manualObject.keySet()){
            map.put(key, manualObject.get(key, Target.UNKNOWN));
        }
        return map;        
    }
    
    public Map<String, Construct> evaluate_helper(BindableEvent e) throws EventException{
        return mixin.evaluate_helper(e);
    }
    
    /**
     * By default, this function triggers the event by calling the mixin
     * handler. If this is not the desired behavior, this method can be overridden
     * in the actual event (if it's an external event, for instance)
     * @param o 
     */
    public void manualTrigger(BindableEvent o){
        mixin.manualTrigger(o);
    }
        
    public void cancel(BindableEvent o, boolean state){
        mixin.cancel(o, state);
    }
    
    public boolean isCancellable(BindableEvent o){
        return mixin.isCancellable(o);
    }

	@Override
    public boolean isCancelled(BindableEvent o) {
        return mixin.isCancelled(o);
    }

	@Override
	public URL getSourceJar() {
		return ClassDiscovery.GetClassContainer(this.getClass());
	}

	/**
	 * Returns true if the event is annotated with @hide
	 * @return 
	 */
	@Override
	public final boolean appearInDocumentation() {
		return this.getClass().getAnnotation(hide.class) != null;
	}
    
}

@@ -3103,16 +3103,237 @@
 abel();%0A
+%09%09%09%09if(label == null)%7B%0A%09%09%09%09%09//Set the permission to global if it's null, since that means%0A%09%09%09%09%09//it wasn't set, and so we aren't in a secured environment anyways.%0A%09%09%09%09%09label = PermissionsResolver.GLOBAL_PERMISSION;%0A%09%09%09%09%7D%0A
 %09%09%09%09env.

Set the label to * if it's null.