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

package com.hypixel.hytale.builtin.adventure.farming;

import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.HarvestingDropType;
import com.hypixel.hytale.server.core.entity.ItemUtils;
import com.hypixel.hytale.server.core.modules.interaction.BlockHarvestUtils;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Store;
import java.util.Map;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.math.vector.Vector3d;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
import com.hypixel.hytale.protocol.Rangef;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingData;
import java.time.Instant;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.GrowthModifierAsset;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.CommandBuffer;

public class FarmingUtil
{
    private static final int MAX_SECONDS_BETWEEN_TICKS = 15;
    private static final int BETWEEN_RANDOM = 10;
    
    public static void tickFarming(final CommandBuffer<ChunkStore> commandBuffer, final BlockChunk blockChunk, final BlockSection blockSection, final Ref<ChunkStore> sectionRef, final Ref<ChunkStore> blockRef, final FarmingBlock farmingBlock, final int x, final int y, final int z, final boolean initialTick) {
        final World world = commandBuffer.getExternalData().getWorld();
        final WorldTimeResource worldTimeResource = world.getEntityStore().getStore().getResource(WorldTimeResource.getResourceType());
        final Instant currentTime = worldTimeResource.getGameTime();
        final BlockType blockType = (farmingBlock.getPreviousBlockType() != null) ? BlockType.getAssetMap().getAsset(farmingBlock.getPreviousBlockType()) : BlockType.getAssetMap().getAsset(blockSection.get(x, y, z));
        if (blockType == null) {
            return;
        }
        if (blockType.getFarming() == null) {
            return;
        }
        final FarmingData farmingConfig = blockType.getFarming();
        if (farmingConfig.getStages() == null) {
            return;
        }
        float currentProgress = farmingBlock.getGrowthProgress();
        int currentStage = (int)currentProgress;
        String currentStageSet = farmingBlock.getCurrentStageSet();
        FarmingStageData[] stages = (FarmingStageData[])((currentStageSet != null) ? ((FarmingStageData[])farmingConfig.getStages().get(currentStageSet)) : null);
        if (stages == null) {
            currentStageSet = farmingConfig.getStartingStageSet();
            if (currentStageSet == null) {
                return;
            }
            farmingBlock.setCurrentStageSet(currentStageSet);
            stages = farmingConfig.getStages().get(currentStageSet);
            blockChunk.markNeedsSaving();
        }
        if (stages == null) {
            return;
        }
        if (currentStage < 0) {
            currentStage = 0;
            currentProgress = 0.0f;
            farmingBlock.setGrowthProgress(0.0f);
        }
        if (currentStage >= stages.length) {
            commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
            return;
        }
        long remainingTimeSeconds = currentTime.getEpochSecond() - farmingBlock.getLastTickGameTime().getEpochSecond();
        final ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
        final int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
        final int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
        final int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
        while (currentStage < stages.length) {
            final FarmingStageData stage = stages[currentStage];
            if (stage.shouldStop(commandBuffer, sectionRef, blockRef, x, y, z)) {
                blockChunk.markNeedsSaving();
                farmingBlock.setGrowthProgress((float)stages.length);
                commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
                break;
            }
            final Rangef range = stage.getDuration();
            if (range == null) {
                blockChunk.markNeedsSaving();
                commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
                break;
            }
            final double rand = HashUtil.random(farmingBlock.getGeneration(), worldX, worldY, worldZ);
            final double baseDuration = range.min + (range.max - range.min) * rand;
            long remainingDurationSeconds = Math.round(baseDuration * (1.0 - currentProgress % 1.0));
            double growthMultiplier = 1.0;
            if (farmingConfig.getGrowthModifiers() != null) {
                for (final String modifierName : farmingConfig.getGrowthModifiers()) {
                    final GrowthModifierAsset modifier = GrowthModifierAsset.getAssetMap().getAsset(modifierName);
                    if (modifier != null) {
                        growthMultiplier *= modifier.getCurrentGrowthMultiplier(commandBuffer, sectionRef, blockRef, x, y, z, initialTick);
                    }
                }
            }
            remainingDurationSeconds = Math.round(remainingDurationSeconds / growthMultiplier);
            if (remainingTimeSeconds < remainingDurationSeconds) {
                currentProgress += (float)(remainingTimeSeconds / (baseDuration / growthMultiplier));
                farmingBlock.setGrowthProgress(currentProgress);
                final long nextGrowthInNanos = (remainingDurationSeconds - remainingTimeSeconds) * 1000000000L;
                final long randCap = (long)((15.0 + 10.0 * HashUtil.random((long)farmingBlock.getGeneration() ^ 0xCAFEBEEFL, worldX, worldY, worldZ)) * world.getTps() * WorldTimeResource.getSecondsPerTick(world) * 1.0E9);
                final long cappedNextGrowthInNanos = Math.min(nextGrowthInNanos, randCap);
                blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), currentTime.plusNanos(cappedNextGrowthInNanos));
                break;
            }
            remainingTimeSeconds -= remainingDurationSeconds;
            currentProgress = (float)(++currentStage);
            farmingBlock.setGrowthProgress(currentProgress);
            blockChunk.markNeedsSaving();
            farmingBlock.setGeneration(farmingBlock.getGeneration() + 1);
            if (currentStage >= stages.length) {
                if (stages[currentStage - 1].implementsShouldStop()) {
                    currentStage = stages.length - 1;
                    farmingBlock.setGrowthProgress((float)currentStage);
                    stages[currentStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[currentStage]);
                }
                else {
                    farmingBlock.setGrowthProgress((float)stages.length);
                    commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
                }
            }
            else {
                farmingBlock.setExecutions(0);
                stages[currentStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[currentStage - 1]);
            }
        }
        farmingBlock.setLastTickGameTime(currentTime);
    }
    
    public static void harvest(@Nonnull final World world, @Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final BlockType blockType, final int rotationIndex, @Nonnull final Vector3i blockPosition) {
        if (world.getGameplayConfig().getWorldConfig().isBlockGatheringAllowed()) {
            harvest0(store, ref, blockType, rotationIndex, blockPosition);
        }
    }
    
    @Nullable
    public static CapturedNPCMetadata generateCapturedNPCMetadata(@Nonnull final ComponentAccessor<EntityStore> componentAccessor, @Nonnull final Ref<EntityStore> entityRef, final int roleIndex) {
        final PersistentModel persistentModel = componentAccessor.getComponent(entityRef, PersistentModel.getComponentType());
        if (persistentModel == null) {
            return null;
        }
        final ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(persistentModel.getModelReference().getModelAssetId());
        final CapturedNPCMetadata meta = new CapturedNPCMetadata();
        if (modelAsset != null) {
            meta.setIconPath(modelAsset.getIcon());
        }
        meta.setRoleIndex(roleIndex);
        return meta;
    }
    
    protected static boolean harvest0(@Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final BlockType blockType, final int rotationIndex, @Nonnull final Vector3i blockPosition) {
        final FarmingData farmingConfig = blockType.getFarming();
        if (farmingConfig == null || farmingConfig.getStages() == null) {
            return false;
        }
        if (blockType.getGathering().getHarvest() == null) {
            return false;
        }
        final World world = store.getExternalData().getWorld();
        final Vector3d centerPosition = new Vector3d();
        blockType.getBlockCenter(rotationIndex, centerPosition);
        centerPosition.add(blockPosition);
        if (farmingConfig.getStageSetAfterHarvest() == null) {
            giveDrops(store, ref, centerPosition, blockType);
            final WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
            if (chunk != null) {
                chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z);
            }
            return true;
        }
        giveDrops(store, ref, centerPosition, blockType);
        final Map<String, FarmingStageData[]> stageSets = farmingConfig.getStages();
        final FarmingStageData[] stages = stageSets.get(farmingConfig.getStartingStageSet());
        if (stages == null) {
            return false;
        }
        final int currentStageIndex = stages.length - 1;
        final FarmingStageData previousStage = stages[currentStageIndex];
        final String newStageSet = farmingConfig.getStageSetAfterHarvest();
        final FarmingStageData[] newStages = stageSets.get(newStageSet);
        if (newStages == null || newStages.length == 0) {
            final WorldChunk chunk2 = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
            if (chunk2 != null) {
                chunk2.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z);
            }
            return false;
        }
        final Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
        final Ref<ChunkStore> chunkRef = world.getChunkStore().getChunkReference(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
        if (chunkRef == null) {
            return false;
        }
        final BlockComponentChunk blockComponentChunk = chunkStore.getComponent(chunkRef, BlockComponentChunk.getComponentType());
        if (blockComponentChunk == null) {
            return false;
        }
        final Instant now = store.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime();
        final int blockIndexColumn = ChunkUtil.indexBlockInColumn(blockPosition.x, blockPosition.y, blockPosition.z);
        Ref<ChunkStore> blockRef = blockComponentChunk.getEntityReference(blockIndexColumn);
        FarmingBlock farmingBlock;
        if (blockRef == null) {
            final Holder<ChunkStore> blockEntity = ChunkStore.REGISTRY.newHolder();
            blockEntity.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(blockIndexColumn, chunkRef));
            farmingBlock = new FarmingBlock();
            farmingBlock.setLastTickGameTime(now);
            farmingBlock.setCurrentStageSet(newStageSet);
            blockEntity.addComponent(FarmingBlock.getComponentType(), farmingBlock);
            blockRef = chunkStore.addEntity(blockEntity, AddReason.SPAWN);
        }
        else {
            farmingBlock = chunkStore.ensureAndGetComponent(blockRef, FarmingBlock.getComponentType());
        }
        farmingBlock.setCurrentStageSet(newStageSet);
        farmingBlock.setGrowthProgress(0.0f);
        farmingBlock.setExecutions(0);
        farmingBlock.setGeneration(farmingBlock.getGeneration() + 1);
        farmingBlock.setLastTickGameTime(now);
        final Ref<ChunkStore> sectionRef = world.getChunkStore().getChunkSectionReference(ChunkUtil.chunkCoordinate(blockPosition.x), ChunkUtil.chunkCoordinate(blockPosition.y), ChunkUtil.chunkCoordinate(blockPosition.z));
        if (sectionRef == null) {
            return false;
        }
        if (blockRef == null) {
            return false;
        }
        final BlockSection section = chunkStore.getComponent(sectionRef, BlockSection.getComponentType());
        if (section != null) {
            section.scheduleTick(ChunkUtil.indexBlock(blockPosition.x, blockPosition.y, blockPosition.z), now);
        }
        newStages[0].apply(chunkStore, sectionRef, blockRef, blockPosition.x, blockPosition.y, blockPosition.z, previousStage);
        return true;
    }
    
    protected static void giveDrops(@Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final Vector3d origin, @Nonnull final BlockType blockType) {
        final HarvestingDropType harvest = blockType.getGathering().getHarvest();
        final String itemId = harvest.getItemId();
        final String dropListId = harvest.getDropListId();
        BlockHarvestUtils.getDrops(blockType, 1, itemId, dropListId).forEach(itemStack -> ItemUtils.interactivelyPickupItem(ref, itemStack, origin, store));
    }
}
