package com.elmakers.mine.bukkit.action;

import com.elmakers.mine.bukkit.api.action.CastContext;
import com.elmakers.mine.bukkit.api.action.SpellAction;
import com.elmakers.mine.bukkit.api.magic.Mage;
import com.elmakers.mine.bukkit.api.spell.Spell;
import com.elmakers.mine.bukkit.api.spell.SpellResult;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public abstract class CompoundAction extends BaseSpellAction
{
    private boolean usesBrush = false;
    private boolean undoable = false;
    private boolean requiresBuildPermission = false;
    private boolean requiresBreakPermission = false;
    protected ConfigurationSection actionConfiguration;
    protected CastContext actionContext;

    protected Map<String, ActionHandler> handlers = new HashMap<String, ActionHandler>();
    protected String currentHandler = null;
    protected State state = State.NOT_STARTED;

    protected enum State {
        NOT_STARTED,
        STARTED,
        STEPPING
    };

    public SpellResult step(CastContext context) {
        return SpellResult.NO_ACTION;
    }

    public SpellResult start(CastContext context) {
        return SpellResult.NO_ACTION;
    }

    public boolean next(CastContext context) {
        return false;
    }

    protected SpellResult startActions() {
        return startActions("actions");
    }

    protected SpellResult startActions(String handlerKey) {
        currentHandler = handlerKey;
        ActionHandler handler = handlers.get(currentHandler);
        if (handler != null) {
            handler.reset(actionContext);
        } else {
            currentHandler = null;
        }

        return SpellResult.NO_ACTION;
    }

    public boolean hasActions() {
        return hasActions("actions");
    }

    public boolean hasActions(String key) {
        ActionHandler handler = handlers.get(key);
        return handler != null && handler.size() > 0;
    }

    @Override
    public SpellResult perform(CastContext context) {
        SpellResult result = SpellResult.NO_ACTION;
        while (!result.isStop()) {
            if (state == State.NOT_STARTED) {
                result = result.min(start(context));

                // Don't continue if the action failed to start
                if (result.isStop() || result.isFailure()) break;
                state = State.STARTED;
            }

            if (state == State.STARTED) {
                result = result.min(step(context));
                if (result.isStop()) break;
                state = State.STEPPING;
            }

            ActionHandler handler = currentHandler == null ? null : handlers.get(currentHandler);
            if (handler != null) {
                result = result.min(handler.perform(actionContext));
                if (result.isStop()) break;
            }

            if (!next(context)) {
                if (handler != null) {
                    handler.finish(actionContext);
                }
                currentHandler = null;
                break;
            }

            result = result.min(step(context));
        }
        return result;
    }

    @Override
    public void reset(CastContext context) {
        super.reset(context);
        if (actionContext == null && context != null) {
            actionContext = new com.elmakers.mine.bukkit.action.CastContext(context);
        }
        state = State.NOT_STARTED;
        currentHandler = null;
    }

    @Override
    public void prepare(CastContext context, ConfigurationSection parameters)
    {
        super.prepare(context, parameters);
        for (ActionHandler handler : handlers.values()) {
            handler.prepare(context, parameters);
        }
    }

    @Override
    public void initialize(Spell spell, ConfigurationSection parameters)
    {
        super.initialize(spell, parameters);
        this.actionConfiguration = parameters;
        usesBrush = false;
        undoable = false;
        requiresBuildPermission = false;
        requiresBreakPermission = false;
        addHandlers();
        for (ActionHandler handler : handlers.values()) {
            handler.initialize(spell, actionConfiguration);
            updateFlags(handler);
        }
    }

    protected void addHandlers() {
        addHandler("actions");
    }

    protected ActionHandler addHandler(String handlerKey) {
        ActionHandler handler = handlers.get(handlerKey);
        if (handler != null) {
            return handler;
        }
        handler = new ActionHandler();
        handlers.put(handlerKey, handler);
        if (actionConfiguration != null && actionConfiguration.contains(handlerKey))
        {
            handler.load(actionConfiguration, handlerKey);
        }

        return handler;
    }

    protected void updateFlags(ActionHandler handler) {
        usesBrush = usesBrush || handler.usesBrush();
        undoable = undoable || handler.isUndoable();
        requiresBuildPermission = requiresBuildPermission || handler.requiresBuildPermission();
        requiresBreakPermission = requiresBreakPermission || handler.requiresBreakPermission();
    }

    // These are here for legacy spell support
    // via programmatic action building
    public void addAction(SpellAction action) {
        addAction(action, null);
    }

    public void addAction(SpellAction action, ConfigurationSection parameters) {
        ActionHandler actions = addHandler("actions");
        if (actions == null) {
            actions = new ActionHandler();
        }
        actions.loadAction(action, parameters);
    }

    @Override
    public void load(Mage mage, ConfigurationSection data)
    {
        for (ActionHandler handler : handlers.values())
        {
            handler.loadData(mage, data);
        }
    }

    @Override
    public void save(Mage mage, ConfigurationSection data)
    {
        for (ActionHandler handler : handlers.values())
        {
            handler.saveData(mage, data);
        }
    }

    @Override
    public boolean isUndoable()
    {
        return undoable;
    }

    @Override
    public boolean usesBrush()
    {
        return usesBrush;
    }

    @Override
    public boolean requiresBuildPermission()
    {
        return requiresBuildPermission;
    }

    @Override
    public boolean requiresBreakPermission()
    {
        return requiresBreakPermission;
    }

    @Override
    public void getParameterNames(Spell spell, Collection<String> parameters)
    {
        super.getParameterNames(spell, parameters);
        for (ActionHandler handler : handlers.values())
        {
            handler.getParameterNames(spell, parameters);
        }
    }

    @Override
    public void getParameterOptions(Spell spell, String parameterKey, Collection<String> examples)
    {
        super.getParameterOptions(spell, parameterKey, examples);
        for (ActionHandler handler : handlers.values())
        {
            handler.getParameterOptions(spell, parameterKey, examples);
        }
    }

    @Override
    public String transformMessage(String message)
    {
        for (ActionHandler handler : handlers.values())
        {
            message = handler.transformMessage(message);
        }
        return message;
    }

    public void createActionContext(CastContext context, Entity sourceEntity, Location sourceLocation) {
        actionContext = new com.elmakers.mine.bukkit.action.CastContext(context, sourceEntity, sourceLocation);
    }

    public void createActionContext(CastContext context, Entity sourceEntity, Location sourceLocation, Entity targetEntity, Location targetLocation) {
        actionContext = new com.elmakers.mine.bukkit.action.CastContext(context, sourceEntity, sourceLocation);
        actionContext.setTargetEntity(targetEntity);
        actionContext.setTargetLocation(targetLocation);
    }

    @Override
    public int getActionCount() {
        int actionCount = 0;
        for (ActionHandler handler : handlers.values())
        {
            actionCount += handler.getActionCount();
        }
        return actionCount;
    }

    protected void skippedActions(CastContext context) {
        skippedActions("actions", context);
    }

    protected void skippedActions(String key, CastContext context) {
        ActionHandler actions = handlers.get(key);
        if (actions == null) return;
        int actionCount = actions.getActionCount();
        context.performedActions(actionCount);
        context.addWork(actionCount);
    }

    @Override
    public Object clone()
    {
        CompoundAction action = (CompoundAction)super.clone();
        if (action != null)
        {
            action.handlers = new HashMap<String, ActionHandler>();
            for (Map.Entry<String, ActionHandler> entry : handlers.entrySet())
            {
                action.handlers.put(entry.getKey(), (ActionHandler)entry.getValue().clone());
            }
        }
        return action;
    }
}

@@ -2926,32 +2926,67 @@
 k;%0A            %7D
+%0A            currentHandler = null;
 %0A%0A            if
@@ -3117,47 +3117,8 @@
   %7D%0A
-                currentHandler = null;%0A
     

Reset CompoundAction handler after it is finished