package net.minecraft.world.entity.vehicle;

import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.phys.Vec3D;
import net.minecraft.world.entity.Entity;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.syncher.DataWatcherObject;

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

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

class EntityBoat extends Entity {
#if version >= 1.17
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_WOOD_TYPE:DATA_ID_TYPE;
#elseif version >= 1.14
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_WOOD_TYPE:e;
#elseif version >= 1.9
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_WOOD_TYPE:d;
#else
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_WOOD_TYPE:###;
#endif

    <code>
    public static final Key<com.bergerkiller.bukkit.common.wrappers.BoatWoodType> DATA_WOOD_TYPE = Key.Type.BOAT_WOOD_TYPE.createKey(T.DATA_WOOD_TYPE, -1);
    </code>
}

// Since 1.20.3. Same data watcher fields as EntityMinecartAbstract.
optional class VehicleEntity extends Entity {
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:DATA_ID_HURT;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:DATA_ID_HURTDIR;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:DATA_ID_DAMAGE;
}

class EntityMinecartAbstract extends Entity {
#if version >= 1.21.5
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:###;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:###;
    private static optional final (DataWatcher.Key<BlockData>) DataWatcherObject<Optional<IBlockData>> DATA_ID_CUSTOM_DISPLAY_BLOCK;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_TYPE:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_OFFSET:DATA_ID_DISPLAY_OFFSET;
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<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>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:###;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:###;
    private static optional final (DataWatcher.Key<BlockData>) DataWatcherObject<Optional<IBlockData>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_TYPE:DATA_ID_DISPLAY_BLOCK;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_OFFSET:DATA_ID_DISPLAY_OFFSET;
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_BLOCK_VISIBLE:DATA_ID_CUSTOM_DISPLAY;
#elseif version >= 1.17
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:DATA_ID_HURT;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:DATA_ID_HURTDIR;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:DATA_ID_DAMAGE;
    private static optional final (DataWatcher.Key<BlockData>) DataWatcherObject<Optional<IBlockData>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_TYPE:DATA_ID_DISPLAY_BLOCK;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_OFFSET:DATA_ID_DISPLAY_OFFSET;
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_BLOCK_VISIBLE:DATA_ID_CUSTOM_DISPLAY;
#elseif version >= 1.14
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:b;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:c;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:d;
    private static optional final (DataWatcher.Key<BlockData>) DataWatcherObject<Optional<IBlockData>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_TYPE:e;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_OFFSET:f;
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_BLOCK_VISIBLE:g;
#elseif version >= 1.9
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:a;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:b;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:c;
    private static optional final (DataWatcher.Key<BlockData>) DataWatcherObject<Optional<IBlockData>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_TYPE:d;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_OFFSET:e;
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_BLOCK_VISIBLE:f;
#else
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_FACTOR:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_SHAKING_DIRECTION:###;
    private static optional final (DataWatcher.Key<Float>) DataWatcherObject<Float> DATA_SHAKING_DAMAGE:###;
    private static optional final (DataWatcher.Key<BlockData>) DataWatcherObject<Optional<IBlockData>> DATA_ID_CUSTOM_DISPLAY_BLOCK:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_TYPE:###;
    private static optional final (DataWatcher.Key<Integer>) DataWatcherObject<Integer> DATA_BLOCK_OFFSET:###;
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<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 (VehicleEntityHandle.T.isAvailable()) {
            DATA_SHAKING_FACTOR = Key.Type.INTEGER.createKey(VehicleEntityHandle.T.DATA_SHAKING_FACTOR, -1);
            DATA_SHAKING_DIRECTION = Key.Type.INTEGER.createKey(VehicleEntityHandle.T.DATA_SHAKING_DIRECTION, -1);
            DATA_SHAKING_DAMAGE = Key.Type.FLOAT.createKey(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.18
    public int getHurtTime();
    public void activate:activateMinecart(int x, int y, int z, boolean active);
#else
    public int getHurtTime:getType();
    public void activate:a(int x, int y, int z, boolean active);
#endif
}

class EntityMinecartRideable extends EntityMinecartAbstract {

}

class EntityMinecartFurnace extends EntityMinecartAbstract {
#if version >= 1.17
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_SMOKING:DATA_ID_FUEL;
#elseif version >= 1.14
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_SMOKING:d;
#elseif version >= 1.9
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<Boolean> DATA_SMOKING:c;
#else
    private static optional final (DataWatcher.Key<Boolean>) DataWatcherObject<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) Vec3D getPushForce() { return instance.push; }
    public void setPushForce(double fx, double fy, double fz) { instance.push = new Vec3D(fx, fy, fz); }
#elseif version >= 1.17
    public (org.bukkit.util.Vector) Vec3D getPushForce() { return new Vec3D(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) Vec3D getPushForce() { return new Vec3D(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) Vec3D getPushForce() { return new Vec3D(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 EntityMinecartHopper extends EntityMinecartAbstract {
#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 EntityMinecartTNT extends EntityMinecartAbstract {
#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.EntityMinecartTNT 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 EntityMinecartCommandBlock extends EntityMinecartAbstract {
#if version >= 1.17
    public static optional final (DataWatcher.Key<String>) DataWatcherObject<String> DATA_COMMAND:DATA_ID_COMMAND_NAME;
    private static optional final (DataWatcher.Key<Object>) DataWatcherObject<IChatBaseComponent> DATA_PREVIOUS_OUTPUT:DATA_ID_LAST_OUTPUT;
#elseif version >= 1.14
    public static optional final (DataWatcher.Key<String>) DataWatcherObject<String> DATA_COMMAND:COMMAND;
    private static optional final (DataWatcher.Key<Object>) DataWatcherObject<IChatBaseComponent> DATA_PREVIOUS_OUTPUT:c;
#elseif version >= 1.9
    // DataWatcherObject constants are available
    #if version >= 1.11
        public static optional final (DataWatcher.Key<String>) DataWatcherObject<String> DATA_COMMAND:COMMAND;
    #else
        public static optional final (DataWatcher.Key<String>) DataWatcherObject<String> DATA_COMMAND:a;
    #endif
    private static optional final (DataWatcher.Key<Object>) DataWatcherObject<IChatBaseComponent> DATA_PREVIOUS_OUTPUT:b;
#else
    // Int keys are used on MC 1.8.8
    public static optional final (DataWatcher.Key<String>) DataWatcherObject<String> DATA_COMMAND:###;
    private static optional final (DataWatcher.Key<Object>) DataWatcherObject<IChatBaseComponent> 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 EntityMinecartMobSpawner extends EntityMinecartAbstract {
#if version >= 1.17
    private final (com.bergerkiller.bukkit.common.wrappers.MobSpawner) net.minecraft.world.level.MobSpawnerAbstract mobSpawner:spawner;
#elseif version >= 1.14
    private final (com.bergerkiller.bukkit.common.wrappers.MobSpawner) net.minecraft.world.level.MobSpawnerAbstract mobSpawner:b;
#else
    private final (com.bergerkiller.bukkit.common.wrappers.MobSpawner) net.minecraft.world.level.MobSpawnerAbstract mobSpawner:a;
#endif
}

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

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