// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.builtin.fluid;

import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.concurrent.Executor;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.util.TargetUtil;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType;
import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntPosition;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
import com.hypixel.hytale.server.core.command.system.arguments.types.AssetArgumentType;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
import com.hypixel.hytale.server.core.command.system.arguments.types.SingleArgumentType;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;

public class FluidCommand extends AbstractCommandCollection
{
    private static final SingleArgumentType<Fluid> FLUID_ARG;
    
    public FluidCommand() {
        super("fluid", "Fluid debug commands");
        this.addSubCommand(new SetCommand());
        this.addSubCommand(new GetCommand());
        this.addSubCommand(new SetRadiusCommand());
    }
    
    static {
        FLUID_ARG = new AssetArgumentType("Fluid", (Class<JsonAssetWithMap>)Fluid.class, "");
    }
    
    public static class SetCommand extends AbstractPlayerCommand
    {
        @Nonnull
        private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK;
        @Nonnull
        private static final Message MESSAGE_COMMANDS_SET_UNKNOWN_FLUID;
        @Nonnull
        private static final Message MESSAGE_COMMANDS_NO_SECTION_COMPONENT;
        @Nonnull
        private final RequiredArg<Fluid> fluid;
        @Nonnull
        private final RequiredArg<Integer> level;
        @Nonnull
        private final OptionalArg<RelativeIntPosition> targetOffset;
        
        public SetCommand() {
            super("set", "Changes the fluid at the target position");
            this.fluid = this.withRequiredArg("fluid", "", FluidCommand.FLUID_ARG);
            this.level = this.withRequiredArg("level", "", ArgTypes.INTEGER);
            this.targetOffset = this.withOptionalArg("offset", "", ArgTypes.RELATIVE_BLOCK_POSITION);
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            final RelativeIntPosition offset = this.targetOffset.get(context);
            final Vector3i blockTarget = TargetUtil.getTargetBlock(ref, 8.0, store);
            if (blockTarget == null) {
                playerRef.sendMessage(SetCommand.MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK);
                return;
            }
            final ChunkStore chunkStore = world.getChunkStore();
            final Vector3i pos = (offset == null) ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore);
            final Fluid fluid = this.fluid.get(context);
            if (fluid == null) {
                playerRef.sendMessage(SetCommand.MESSAGE_COMMANDS_SET_UNKNOWN_FLUID);
                return;
            }
            Integer level = this.level.get(context);
            if (level > fluid.getMaxFluidLevel()) {
                level = fluid.getMaxFluidLevel();
                playerRef.sendMessage(Message.translation("server.commands.set.maxFluidLevelClamped").param("level", fluid.getMaxFluidLevel()));
            }
            final Integer finalLevel = level;
            chunkStore.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(pos.x), ChunkUtil.chunkCoordinate(pos.y), ChunkUtil.chunkCoordinate(pos.z)).thenAcceptAsync(section -> {
                final Store<ChunkStore> sectionStore = section.getStore();
                final FluidSection fluidSection = sectionStore.getComponent(section, FluidSection.getComponentType());
                if (fluidSection == null) {
                    playerRef.sendMessage(SetCommand.MESSAGE_COMMANDS_NO_SECTION_COMPONENT);
                }
                else {
                    final int index = ChunkUtil.indexBlock(pos.x, pos.y, pos.z);
                    fluidSection.setFluid(index, fluid, finalLevel.byteValue());
                    playerRef.sendMessage(Message.translation("server.commands.set.success").param("x", pos.x).param("y", pos.y).param("z", pos.z).param("id", fluid.getId()).param("level", finalLevel));
                    final ChunkSection chunkSection = sectionStore.getComponent(section, ChunkSection.getComponentType());
                    final WorldChunk worldChunk = sectionStore.getComponent(chunkSection.getChunkColumnReference(), WorldChunk.getComponentType());
                    worldChunk.markNeedsSaving();
                    worldChunk.setTicking(pos.x, pos.y, pos.z, true);
                }
            }, (Executor)world);
        }
        
        static {
            MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK = Message.translation("server.commands.errors.playerNotLookingAtBlock");
            MESSAGE_COMMANDS_SET_UNKNOWN_FLUID = Message.translation("server.commands.set.unknownFluid");
            MESSAGE_COMMANDS_NO_SECTION_COMPONENT = Message.translation("server.commands.noSectionComponent");
        }
    }
    
    public static class GetCommand extends AbstractPlayerCommand
    {
        @Nonnull
        private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK;
        @Nonnull
        private static final Message MESSAGE_COMMANDS_NO_SECTION_COMPONENT;
        @Nonnull
        private final OptionalArg<RelativeIntPosition> targetOffset;
        
        public GetCommand() {
            super("get", "Gets the fluid at the target position");
            this.targetOffset = this.withOptionalArg("offset", "", ArgTypes.RELATIVE_BLOCK_POSITION);
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            final RelativeIntPosition offset = this.targetOffset.get(context);
            final Vector3i blockTarget = TargetUtil.getTargetBlock(ref, 8.0, store);
            if (blockTarget == null) {
                playerRef.sendMessage(GetCommand.MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK);
                return;
            }
            final ChunkStore chunkStore = world.getChunkStore();
            final Vector3i pos = (offset == null) ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore);
            chunkStore.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(pos.x), ChunkUtil.chunkCoordinate(pos.y), ChunkUtil.chunkCoordinate(pos.z)).thenAcceptAsync(section -> {
                final Store<ChunkStore> sectionStore = section.getStore();
                final FluidSection fluidSection = sectionStore.getComponent(section, FluidSection.getComponentType());
                if (fluidSection == null) {
                    playerRef.sendMessage(GetCommand.MESSAGE_COMMANDS_NO_SECTION_COMPONENT);
                }
                else {
                    final int index = ChunkUtil.indexBlock(pos.x, pos.y, pos.z);
                    final Fluid fluid = fluidSection.getFluid(index);
                    final byte level = fluidSection.getFluidLevel(index);
                    playerRef.sendMessage(Message.translation("server.commands.get.success").param("x", pos.x).param("y", pos.y).param("z", pos.z).param("id", fluid.getId()).param("level", level));
                }
            }, (Executor)world);
        }
        
        static {
            MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK = Message.translation("server.commands.errors.playerNotLookingAtBlock");
            MESSAGE_COMMANDS_NO_SECTION_COMPONENT = Message.translation("server.commands.noSectionComponent");
        }
    }
    
    public static class SetRadiusCommand extends AbstractPlayerCommand
    {
        @Nonnull
        private static final Message MESSAGE_COMMANDS_SET_UNKNOWN_FLUID;
        @Nonnull
        private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK;
        @Nonnull
        private final RequiredArg<Integer> radius;
        @Nonnull
        private final RequiredArg<Fluid> fluid;
        @Nonnull
        private final RequiredArg<Integer> level;
        @Nonnull
        private final OptionalArg<RelativeIntPosition> targetOffset;
        
        public SetRadiusCommand() {
            super("setradius", "Changes the fluid at the player position in a given radius");
            this.radius = this.withRequiredArg("radius", "", ArgTypes.INTEGER);
            this.fluid = this.withRequiredArg("fluid", "", FluidCommand.FLUID_ARG);
            this.level = this.withRequiredArg("level", "", ArgTypes.INTEGER);
            this.targetOffset = this.withOptionalArg("offset", "", ArgTypes.RELATIVE_BLOCK_POSITION);
        }
        
        @Override
        protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
            final RelativeIntPosition offset = this.targetOffset.get(context);
            final Vector3i blockTarget = TargetUtil.getTargetBlock(ref, 8.0, store);
            if (blockTarget == null) {
                playerRef.sendMessage(SetRadiusCommand.MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK);
                return;
            }
            final ChunkStore chunkStore = world.getChunkStore();
            final Vector3i pos = (offset == null) ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore);
            final Fluid fluid = this.fluid.get(context);
            if (fluid == null) {
                playerRef.sendMessage(SetRadiusCommand.MESSAGE_COMMANDS_SET_UNKNOWN_FLUID);
                return;
            }
            Integer level = this.level.get(context);
            if (level > fluid.getMaxFluidLevel()) {
                level = fluid.getMaxFluidLevel();
                playerRef.sendMessage(Message.translation("server.commands.set.maxFluidLevelClamped").param("level", fluid.getMaxFluidLevel()));
            }
            final Integer radius = this.radius.get(context);
            final int minX = pos.x - radius;
            final int maxX = pos.x + radius;
            final int minY = pos.y - radius;
            final int maxY = pos.y + radius;
            final int minZ = pos.z - radius;
            final int maxZ = pos.z + radius;
            final int minCX = ChunkUtil.chunkCoordinate(minX);
            final int maxCX = ChunkUtil.chunkCoordinate(maxX);
            final int minCY = ChunkUtil.chunkCoordinate(minY);
            final int maxCY = ChunkUtil.chunkCoordinate(maxY);
            final int minCZ = ChunkUtil.chunkCoordinate(minZ);
            final int maxCZ = ChunkUtil.chunkCoordinate(maxZ);
            final Integer finalLevel = level;
            for (int cx = minCX; cx <= maxCX; ++cx) {
                for (int cz = minCZ; cz <= maxCZ; ++cz) {
                    final int relMinX = MathUtil.clamp(minX - ChunkUtil.minBlock(cx), 0, 32);
                    final int relMaxX = MathUtil.clamp(maxX - ChunkUtil.minBlock(cx), 0, 32);
                    final int relMinZ = MathUtil.clamp(minZ - ChunkUtil.minBlock(cz), 0, 32);
                    final int relMaxZ = MathUtil.clamp(maxZ - ChunkUtil.minBlock(cz), 0, 32);
                    for (int cy = minCY; cy <= maxCY; ++cy) {
                        chunkStore.getChunkSectionReferenceAsync(cx, cy, cz).thenAcceptAsync(section -> {
                            final Store<ChunkStore> sectionStore = section.getStore();
                            final FluidSection fluidSection = sectionStore.getComponent(section, FluidSection.getComponentType());
                            if (fluidSection == null) {
                                return;
                            }
                            else {
                                final int relMinY = MathUtil.clamp(minY - ChunkUtil.minBlock(fluidSection.getY()), 0, 32);
                                final int relMaxY = MathUtil.clamp(maxY - ChunkUtil.minBlock(fluidSection.getY()), 0, 32);
                                final ChunkSection sectionComp = sectionStore.getComponent(section, ChunkSection.getComponentType());
                                final WorldChunk worldChunk = sectionStore.getComponent(sectionComp.getChunkColumnReference(), WorldChunk.getComponentType());
                                for (int y = relMinY; y < relMaxY; ++y) {
                                    for (int z = relMinZ; z < relMaxZ; ++z) {
                                        for (int x = relMinX; x < relMaxX; ++x) {
                                            final int index = ChunkUtil.indexBlock(x, y, z);
                                            fluidSection.setFluid(index, fluid, finalLevel.byteValue());
                                            worldChunk.setTicking(pos.x, pos.y, pos.z, true);
                                        }
                                    }
                                }
                                worldChunk.markNeedsSaving();
                                return;
                            }
                        }, (Executor)world);
                    }
                }
            }
        }
        
        static {
            MESSAGE_COMMANDS_SET_UNKNOWN_FLUID = Message.translation("server.commands.set.unknownFluid");
            MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK = Message.translation("server.commands.errors.playerNotLookingAtBlock");
        }
    }
}
