package net.minecraft.world.entity.vehicle.minecart;

import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.Entity;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;

import com.bergerkiller.generated.net.minecraft.world.entity.vehicle.minecart.NewMinecartBehaviorHandle.MinecartStepHandle;

import com.bergerkiller.bukkit.common.wrappers.BlockData;
import com.bergerkiller.bukkit.common.wrappers.DataWatcher;
import com.bergerkiller.bukkit.common.wrappers.DataWatcher.Key;

class AbstractMinecart extends Entity {
#if version >= 1.21.5
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_FACTOR:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_DIRECTION:###;
    private static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_SHAKING_DAMAGE:###;
    private static optional final (DataWatcher.Key<BlockData>) EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_TYPE:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_OFFSET:DATA_ID_DISPLAY_OFFSET;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_BLOCK_VISIBLE:###;
#elseif version >= 1.20.3
    // Note: hurt/hurtdir/damage have been moved to parent class "VehicleEntity"
    //       it's a bit of a mess with backwards compatibility unfortunately
    //       we cope...
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_FACTOR:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_DIRECTION:###;
    private static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_SHAKING_DAMAGE:###;
    private static optional final (DataWatcher.Key<BlockData>) EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_TYPE:DATA_ID_DISPLAY_BLOCK;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_OFFSET:DATA_ID_DISPLAY_OFFSET;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_BLOCK_VISIBLE:DATA_ID_CUSTOM_DISPLAY;
#elseif version >= 1.17
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_FACTOR:DATA_ID_HURT;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_DIRECTION:DATA_ID_HURTDIR;
    private static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_SHAKING_DAMAGE:DATA_ID_DAMAGE;
    private static optional final (DataWatcher.Key<BlockData>) EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_TYPE:DATA_ID_DISPLAY_BLOCK;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_OFFSET:DATA_ID_DISPLAY_OFFSET;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_BLOCK_VISIBLE:DATA_ID_CUSTOM_DISPLAY;
#elseif version >= 1.14
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_FACTOR:b;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_DIRECTION:c;
    private static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_SHAKING_DAMAGE:d;
    private static optional final (DataWatcher.Key<BlockData>) EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_TYPE:e;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_OFFSET:f;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_BLOCK_VISIBLE:g;
#elseif version >= 1.9
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_FACTOR:a;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_DIRECTION:b;
    private static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_SHAKING_DAMAGE:c;
    private static optional final (DataWatcher.Key<BlockData>) EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_TYPE:d;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_OFFSET:e;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_BLOCK_VISIBLE:f;
#else
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_FACTOR:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_SHAKING_DIRECTION:###;
    private static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_SHAKING_DAMAGE:###;
    private static optional final (DataWatcher.Key<BlockData>) EntityDataAccessor<Optional<BlockState>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_TYPE:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_BLOCK_OFFSET:###;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_BLOCK_VISIBLE:###;
#endif

    <code>
    public static final Key<Integer> DATA_SHAKING_FACTOR;
    public static final Key<Integer> DATA_SHAKING_DIRECTION;
    public static final Key<Float> DATA_SHAKING_DAMAGE;
    public static final Key<BlockData> DATA_CUSTOM_DISPLAY_BLOCK;
    public static final Key<Integer> DATA_BLOCK_TYPE;
    public static final Key<Integer> DATA_BLOCK_OFFSET = Key.Type.INTEGER.createKey(T.DATA_BLOCK_OFFSET, 21);
    public static final Key<Boolean> DATA_BLOCK_VISIBLE;

    static {
        if (com.bergerkiller.generated.net.minecraft.world.entity.vehicle.VehicleEntityHandle.T.isAvailable()) {
            DATA_SHAKING_FACTOR = Key.Type.INTEGER.createKey(com.bergerkiller.generated.net.minecraft.world.entity.vehicle.VehicleEntityHandle.T.DATA_SHAKING_FACTOR, -1);
            DATA_SHAKING_DIRECTION = Key.Type.INTEGER.createKey(com.bergerkiller.generated.net.minecraft.world.entity.vehicle.VehicleEntityHandle.T.DATA_SHAKING_DIRECTION, -1);
            DATA_SHAKING_DAMAGE = Key.Type.FLOAT.createKey(com.bergerkiller.generated.net.minecraft.world.entity.vehicle.VehicleEntityHandle.T.DATA_SHAKING_DAMAGE, -1);
        } else {
            DATA_SHAKING_FACTOR = Key.Type.INTEGER.createKey(T.DATA_SHAKING_FACTOR, 17);
            DATA_SHAKING_DIRECTION = Key.Type.INTEGER.createKey(T.DATA_SHAKING_DIRECTION, 18);
            DATA_SHAKING_DAMAGE = Key.Type.FLOAT.createKey(T.DATA_SHAKING_DAMAGE, 19);
        }

        if (com.bergerkiller.bukkit.common.internal.CommonCapabilities.IS_MINECART_BLOCK_COMBINED_KEY) {
            DATA_CUSTOM_DISPLAY_BLOCK = Key.Type.BLOCK_DATA.createKey(T.DATA_ID_CUSTOM_DISPLAY_BLOCK, -1);
            DATA_BLOCK_TYPE = new Key.Disabled(Key.Type.INTEGER);
            DATA_BLOCK_VISIBLE = new Key.Disabled(Key.Type.BOOLEAN);
        } else {
            DATA_CUSTOM_DISPLAY_BLOCK = new Key.Disabled(Key.Type.BLOCK_DATA);
            DATA_BLOCK_TYPE = Key.Type.INTEGER.createKey(T.DATA_BLOCK_TYPE, 20);
            DATA_BLOCK_VISIBLE = Key.Type.BOOLEAN.createKey(T.DATA_BLOCK_VISIBLE, 22);
        }
    }
    </code>

    public float getDamage();
    public void setDamage(float damage);

#if version >= 1.21.11
    public void activateMinecart((org.bukkit.World) net.minecraft.server.level.ServerLevel world, int x, int y, int z, boolean active);
#else
    public void activateMinecart((org.bukkit.World) net.minecraft.server.level.ServerLevel world, int x, int y, int z, boolean active) {
  #if version >= 1.18
        instance.activateMinecart(x, y, z, active);
  #else
        instance.a(x, y, z, active);
  #endif
    }
#endif

#if version >= 1.18
    public int getHurtTime();
#else
    public int getHurtTime:getType();
#endif
}

class Minecart extends AbstractMinecart {

}

class MinecartFurnace extends AbstractMinecart {
#if version >= 1.17
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_SMOKING:DATA_ID_FUEL;
#elseif version >= 1.14
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_SMOKING:d;
#elseif version >= 1.9
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_SMOKING:c;
#else
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_SMOKING:###;
#endif

    <code>
    public static final Key<Boolean> DATA_SMOKING = Key.Type.BOOLEAN.createKey(T.DATA_SMOKING, 16);
    </code>

#if version >= 1.16.4
    public int fuel;
#elseif version >= 1.14
    private int fuel:e;
#elseif version >= 1.9
    private int fuel:d;
#else
    private int fuel:c;
#endif

#if version >= 1.21.2
    public (org.bukkit.util.Vector) Vec3 getPushForce() { return instance.push; }
    public void setPushForce(double fx, double fy, double fz) { instance.push = new Vec3(fx, fy, fz); }
#elseif version >= 1.17
    public (org.bukkit.util.Vector) Vec3 getPushForce() { return new Vec3(instance.xPush, 0.0, instance.zPush); }
    public void setPushForce(double fx, double fy, double fz) { instance.xPush = fx; instance.zPush = fz; }
#elseif version >= 1.14
    public (org.bukkit.util.Vector) Vec3 getPushForce() { return new Vec3(instance.b, 0.0, instance.c); }
    public void setPushForce(double fx, double fy, double fz) { instance.b = fx; instance.c = fz; }
#else
    public (org.bukkit.util.Vector) Vec3 getPushForce() { return new Vec3(instance.a, 0.0, instance.b); }
    public void setPushForce(double fx, double fy, double fz) { instance.a = fx; instance.b = fz; }
#endif

    <code>
    public void setPushForce(org.bukkit.util.Vector force) {
        setPushForce(force.getX(), force.getY(), force.getZ());
    }

    @Deprecated
    public double getPushForceX() {
        return getPushForce().getX();
    }

    @Deprecated
    public double getPushForceZ() {
        return getPushForce().getZ();
    }

    @Deprecated
    public void setPushForceX(double x) {
        Vector v = getPushForce();
        setPushForce(x, v.getY(), v.getZ());
    }

    @Deprecated
    public void setPushForceZ(double z) {
        Vector v = getPushForce();
        setPushForce(v.getX(), v.getY(), z);
    }
    </code>
}

class MinecartHopper extends AbstractMinecart {
#if version >= 1.18
    public boolean suckItems:suckInItems();
#elseif version >= 1.16
    public boolean suckItems:B();
#elseif version >= 1.14
    public boolean suckItems:C();
#elseif version >= 1.13
    public boolean suckItems:J();
#elseif version >= 1.12
    public boolean suckItems:H();
#elseif version >= 1.9
    public boolean suckItems:I();
#else
    public boolean suckItems:D();
#endif

#if version >= 1.9.4
    public boolean isSuckingEnabled:isEnabled();
    public void setSuckingEnabled:setEnabled(boolean enabled);
#elseif version >= 1.9
    public boolean isSuckingEnabled:C();
    public void setSuckingEnabled:k(boolean enabled);
#else
    public boolean isSuckingEnabled:y();
    public void setSuckingEnabled:i(boolean enabled);
#endif
}

class MinecartTNT extends AbstractMinecart {
#if version >= 1.17
    private int fuse;
#elseif version >= 1.14
    private int fuse:b;
#else
    private int fuse:a;
#endif

#if version >= 1.21.9
    public void explode(double damage) {
        instance.explode((net.minecraft.world.damagesource.DamageSource) null, damage);
    }
#elseif exists net.minecraft.world.entity.vehicle.MinecartTNT public void explode(double damage);
    public void explode(double damage);
#elseif version >= 1.18
    protected void explode(double damage);
#elseif version >= 1.15
    protected void explode:h(double damage);
#elseif version >= 1.9
    protected void explode:c(double damage);
#else
    protected void explode:b(double damage);
#endif

#if version >= 1.21.5
    public void prime() {
        instance.primeFuse((net.minecraft.world.damagesource.DamageSource) null);
    }
#elseif version >= 1.18
    public void prime:primeFuse();
#elseif version >= 1.17
    public void prime:w();
#elseif version >= 1.16
    public void prime:u();
#elseif version >= 1.14
    public void prime:v();
#elseif version >= 1.13
    public void prime:f();
#else
    public void prime:j();
#endif
}

class MinecartCommandBlock extends AbstractMinecart {
#if version >= 1.17
    public static optional final (DataWatcher.Key<String>) EntityDataAccessor<String> DATA_COMMAND:DATA_ID_COMMAND_NAME;
    private static optional final (DataWatcher.Key<Object>) EntityDataAccessor<Component> DATA_PREVIOUS_OUTPUT:DATA_ID_LAST_OUTPUT;
#elseif version >= 1.14
    public static optional final (DataWatcher.Key<String>) EntityDataAccessor<String> DATA_COMMAND:COMMAND;
    private static optional final (DataWatcher.Key<Object>) EntityDataAccessor<Component> DATA_PREVIOUS_OUTPUT:c;
#elseif version >= 1.9
    // EntityDataAccessor constants are available
    #if version >= 1.11
        public static optional final (DataWatcher.Key<String>) EntityDataAccessor<String> DATA_COMMAND:COMMAND;
    #else
        public static optional final (DataWatcher.Key<String>) EntityDataAccessor<String> DATA_COMMAND:a;
    #endif
    private static optional final (DataWatcher.Key<Object>) EntityDataAccessor<Component> DATA_PREVIOUS_OUTPUT:b;
#else
    // Int keys are used on MC 1.8.8
    public static optional final (DataWatcher.Key<String>) EntityDataAccessor<String> DATA_COMMAND:###;
    private static optional final (DataWatcher.Key<Object>) EntityDataAccessor<Component> DATA_PREVIOUS_OUTPUT:###;
#endif
    <code>
    public static final Key<String> DATA_COMMAND = Key.Type.STRING.createKey(T.DATA_COMMAND, 23);
    public static final Key<com.bergerkiller.bukkit.common.wrappers.ChatText> DATA_PREVIOUS_OUTPUT = Key.Type.CHAT_TEXT.createKey(T.DATA_PREVIOUS_OUTPUT, 24);
    </code>
}

class MinecartSpawner extends AbstractMinecart {
#if version >= 1.17
    private final (com.bergerkiller.bukkit.common.wrappers.MobSpawner) net.minecraft.world.level.BaseSpawner mobSpawner:spawner;
#elseif version >= 1.14
    private final (com.bergerkiller.bukkit.common.wrappers.MobSpawner) net.minecraft.world.level.BaseSpawner mobSpawner:b;
#else
    private final (com.bergerkiller.bukkit.common.wrappers.MobSpawner) net.minecraft.world.level.BaseSpawner mobSpawner:a;
#endif
}

// Since Minecraft 1.21.2
optional class NewMinecartBehavior {
    optional class NewMinecartBehavior.MinecartStep {
        public (org.bukkit.util.Vector) Vec3 getPosition:position();
        public (org.bukkit.util.Vector) Vec3 getMovement:movement();
        public float getYaw:yRot();
        public float getPitch:xRot();
        public float getWeight:weight();

        public static (NewMinecartBehaviorHandle.MinecartStepHandle) NewMinecartBehavior.MinecartStep createNew((org.bukkit.util.Vector) Vec3 position, (org.bukkit.util.Vector) Vec3 movement, float yaw, float pitch, float weight) {
            return new NewMinecartBehavior$MinecartStep(position, movement, yaw, pitch, weight);
        }
    }
}
