package me.km.snuviscript.commands;

import me.hammerle.snuviscript.code.ScriptManager;
import me.km.utils.Location;
import me.km.utils.LocationIterator;
import me.km.world.WorldManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;

public class LocationCommands {
    @SuppressWarnings("")
    public static void registerFunctions(ScriptManager sm) {
        sm.registerFunction("loc.new", (sc, in) -> {
            if(in.length >= 6) {
                return new Location((World) in[0].get(sc),
                        in[1].getDouble(sc), in[2].getDouble(sc), in[3].getDouble(sc),
                        in[4].getFloat(sc), in[5].getFloat(sc));
            }
            return new Location((World) in[0].get(sc), in[1].getDouble(sc), in[2].getDouble(sc), in[3].getDouble(sc), 0, 0);
        });
        sm.registerConsumer("loc.setblockpos", (sc, in) -> {
            Location l = (Location) in[0].get(sc);
            BlockPos pos = (BlockPos) in[1].get(sc);
            l.set(pos.getX(), pos.getY(), pos.getZ());
        });
        sm.registerFunction("loc.getx", (sc, in) -> ((Location) in[0].get(sc)).getX());
        sm.registerFunction("loc.gety", (sc, in) -> ((Location) in[0].get(sc)).getY());
        sm.registerFunction("loc.getz", (sc, in) -> ((Location) in[0].get(sc)).getZ());
        sm.registerConsumer("loc.set", (sc, in) -> ((Location) in[0].get(sc)).set(in[1].getDouble(sc), in[2].getDouble(sc), in[3].getDouble(sc)));
        sm.registerConsumer("loc.setx", (sc, in) -> ((Location) in[0].get(sc)).setX(in[1].getDouble(sc)));
        sm.registerConsumer("loc.sety", (sc, in) -> ((Location) in[0].get(sc)).setY(in[1].getDouble(sc)));
        sm.registerConsumer("loc.setz", (sc, in) -> ((Location) in[0].get(sc)).setZ(in[1].getDouble(sc)));
        sm.registerConsumer("loc.add", (sc, in) -> ((Location) in[0].get(sc)).add(in[1].getDouble(sc), in[2].getDouble(sc), in[3].getDouble(sc)));
        sm.registerConsumer("loc.addx", (sc, in) -> ((Location) in[0].get(sc)).addX(in[1].getDouble(sc)));
        sm.registerConsumer("loc.addy", (sc, in) -> ((Location) in[0].get(sc)).addY(in[1].getDouble(sc)));
        sm.registerConsumer("loc.addz", (sc, in) -> ((Location) in[0].get(sc)).addZ(in[1].getDouble(sc)));
        sm.registerConsumer("loc.setyaw", (sc, in) -> ((Location) in[0].get(sc)).setYaw(in[1].getFloat(sc)));
        sm.registerFunction("loc.getyaw", (sc, in) -> (double) ((Location) in[0].get(sc)).getYaw());
        sm.registerConsumer("loc.setpitch", (sc, in) -> ((Location) in[0].get(sc)).setPitch(in[1].getFloat(sc)));
        sm.registerFunction("loc.getpitch", (sc, in) -> (double) ((Location) in[0].get(sc)).getPitch());
        sm.registerFunction("loc.getworld", (sc, in) -> ((Location) in[0].get(sc)).getWorld());
        sm.registerFunction("loc.distance", (sc, in) -> ((Location) in[0].get(sc)).getPos().distanceTo(((Location) in[1].get(sc)).getPos()));
        sm.registerFunction("loc.mod", (sc, in) -> ((Location) in[0].get(sc)).copyAdd(in[1].getDouble(sc), in[2].getDouble(sc), in[3].getDouble(sc)));
        sm.registerFunction("loc.getcoord", (sc, in) -> {
            Location l = (Location) in[0].get(sc);
            switch(in[1].getString(sc)) {
                case "x":
                    return l.getX();
                case "y":
                    return l.getY();
                case "z":
                    return l.getZ();
                case "bx":
                    return (double) MathHelper.floor(l.getX());
                case "by":
                    return (double) MathHelper.floor(l.getY());
                case "bz":
                    return (double) MathHelper.floor(l.getZ());
                case "w":
                    return WorldManager.getName(l.getWorld());
                default:
                    return null;
            }
        });
        sm.registerFunction("loc.isbetween", (sc, in) -> {
            Location l1 = (Location) in[0].get(sc);
            Location l2 = (Location) in[1].get(sc);
            Location l3 = (Location) in[2].get(sc);
            return l1.getX() >= Math.min(l2.getX(), l3.getX()) && l1.getX() <= Math.max(l2.getX(), l3.getX())
                    && l1.getY() >= Math.min(l2.getY(), l3.getY()) && l1.getY() <= Math.max(l2.getY(), l3.getY())
                    && l1.getZ() >= Math.min(l2.getZ(), l3.getZ()) && l1.getZ() <= Math.max(l2.getZ(), l3.getZ());
        });
        sm.registerConsumer("loc.sort", (sc, in) -> {
            Location l1 = (Location) in[0].get(sc);
            Location l2 = (Location) in[1].get(sc);
            if(l1.getX() > l2.getX()) {
                double tmp = l1.getX();
                l1.setX(l2.getX());
                l2.setX(tmp);
            }
            if(l1.getY() > l2.getY()) {
                double tmp = l1.getY();
                l1.setY(l2.getY());
                l2.setY(tmp);
            }
            if(l1.getZ() > l2.getZ()) {
                double tmp = l1.getZ();
                l1.setZ(l2.getZ());
                l2.setZ(tmp);
            }
        });
        sm.registerFunction("loc.iterator", (sc, in) -> {
            return new LocationIterator((IWorld) in[0].get(sc),
                    in[1].getInt(sc), in[2].getInt(sc), in[3].getInt(sc),
                    in[4].getInt(sc), in[5].getInt(sc), in[6].getInt(sc));
        });
        sm.registerFunction("loc.trace", (sc, in) -> {
            Location l = (Location) in[0].get(sc);
            IWorld w = l.getWorld();
            double x = l.getX();
            double y = l.getY();
            double z = l.getZ();
            BlockPos.Mutable pos = new BlockPos.Mutable(x, y, z);
            double ux = in[1].getDouble(sc);
            double uy = in[2].getDouble(sc);
            double uz = in[3].getDouble(sc);
            int steps = in[4].getInt(sc);
            boolean last = in[5].getBoolean(sc);
            for(int i = 0; i < steps; i++) {
                if(!w.isAirBlock(pos)) {
                    if(last) {
                        x -= ux;
                        y -= uy;
                        z -= uz;
                    }
                    l.set(x, y, z);
                    return true;
                }
                x += ux;
                y += uy;
                z += uz;
                pos.setPos(x, y, z);
            }
            return false;
        });
    }
}
