package net.minecraft.world.entity.vehicle;

import net.minecraft.world.entity.Entity;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.syncher.DataWatcherObject;

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.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<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<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<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<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<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;

    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);
        }
    }

    public static final Key<Integer> DATA_BLOCK_TYPE = Key.Type.INTEGER.createKey(T.DATA_BLOCK_TYPE, 20);
    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 = 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.17
    public double pushForceX:xPush;
    public double pushForceZ:zPush;
#elseif version >= 1.14
    public double pushForceX:b;
    public double pushForceZ:c;
#else
    public double pushForceX:a;
    public double pushForceZ:b;
#endif
}

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 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.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
}
