package com.shanebeestudios.skbee.elements.switchcase.sections;

import ch.njol.skript.Skript;
import ch.njol.skript.config.Node;
import ch.njol.skript.config.SectionNode;
import ch.njol.skript.expressions.base.SectionExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.Trigger;
import ch.njol.skript.lang.TriggerItem;
import ch.njol.skript.util.LiteralUtils;
import ch.njol.skript.variables.Variables;
import ch.njol.util.Kleenean;
import com.github.shanebeee.skr.Registration;
import com.shanebeestudios.skbee.config.SkBeeMetrics;
import com.shanebeestudios.skbee.elements.switchcase.events.SwitchReturnEvent;
import com.shanebeestudios.skbee.elements.switchcase.events.SwitchSecEvent;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public class SecExprSwitchReturn extends SectionExpression<Object> {

    public static void register(Registration reg) {
        reg.newCombinedExpression(SecExprSwitchReturn.class, Object.class,
                "(switch return|return switch) %object%")
            .name("SwitchCase - Switch Return")
            .description("Switch an object and have it return a value.")
            .examples("function getRoman(i: number) :: string:",
                "\treturn switch return {_i}:",
                "\t\tcase 1 -> \"I\"",
                "\t\tcase 2 -> \"II\"",
                "\t\tcase 3 -> \"III\"",
                "\t\tcase 4 -> \"IV\"",
                "\t\tcase 5 -> \"V\"",
                "\t\tdefault -> \"potato\"",
                "",
                "function getName(e: entity) :: string:",
                "\treturn switch return {_e}:",
                "\t\tcase sheep -> \"Mr Sheepy\"",
                "\t\tcase cow -> \"Mr Cow\"",
                "\t\tcase pig -> \"Señor Pig\"",
                "\t\tdefault -> strict proper case \"%type of {_e}%\"",
                "",
                "on break:",
                "\tset {_i} to switch return event-block:",
                "\t\tcase stone -> \"Stoney Stone\"",
                "\t\tcase dirt -> \"Dirty Dirt\"",
                "\t\tcase grass block -> \"Grassy Grass\"",
                "\t\tdefault:",
                "\t\t\tif gamemode of player = creative:",
                "\t\t\t\treturn \"&c%type of switched object%\"",
                "\t\t\telse:",
                "\t\t\t\treturn \"&a%type of switched object%\"",
                "",
                "\tsend \"Broken: %{_i}%\" to player")
            .since("3.8.0")
            .register();
    }

    private boolean isSingle = true;
    private Expression<?> switchedObject;
    private Trigger caseSection;

    @SuppressWarnings({"unchecked", "DataFlowIssue"})
    @Override
    public boolean init(Expression<?>[] exprs, int pattern, Kleenean delayed, ParseResult result, @Nullable SectionNode sectionNode, @Nullable List<TriggerItem> triggerItems) {
        if (sectionNode == null) return false;
        SkBeeMetrics.Features.SWITCH_CASE.used();
        this.switchedObject = LiteralUtils.defendExpression(exprs[0]);

        Class<? extends Event>[] currentEvents = getParser().getCurrentEvents();
        Class<? extends Event>[] events = new Class[currentEvents.length + 1];
        System.arraycopy(currentEvents, 0, events, 0, currentEvents.length);
        events[currentEvents.length] = SwitchSecEvent.class;
        this.caseSection = loadCode(sectionNode, "switch section expression", null, null, events);

        // Search through the section and see if the lines are cases
        for (Node node : sectionNode) {
            String key = node.getKey();
            if (!key.startsWith("case") && !key.startsWith("default")) {
                Skript.error("Only cases can be used in a switch section but found this:");
                this.caseSection = null;
                break;
            }
        }

        return LiteralUtils.canInitSafely(this.switchedObject);
    }

    @Override
    protected Object @Nullable [] get(Event event) {
        Object object = this.switchedObject.getSingle(event);
        if (object == null) return null;

        Object variables = Variables.copyLocalVariables(event);
        SwitchReturnEvent returnEvent = new SwitchReturnEvent(object, event);
        Variables.setLocalVariables(returnEvent, variables);
        Trigger.walk(this.caseSection, returnEvent);
        Variables.setLocalVariables(event, Variables.copyLocalVariables(returnEvent));
        Variables.removeLocals(returnEvent);

        return returnEvent.getReturnedObject();
    }

    public Expression<?> getSwitchedObjectExpression() {
        return this.switchedObject;
    }

    public void setIsSingle(boolean isSingle) {
        this.isSingle = isSingle;
    }

    @Override
    public boolean isSingle() {
        return this.isSingle;
    }

    @Override
    public Class<?> getReturnType() {
        return Object.class;
    }

    @Override
    public String toString(Event e, boolean d) {
        return "switch return " + this.switchedObject.toString(e, d);
    }

}
