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

package com.hypixel.hytale.builtin.beds.sleep.systems.player;

import java.lang.invoke.CallSite;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.invoke.MethodHandle;
import java.lang.runtime.SwitchBootstraps;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Comparator;
import java.util.Collection;
import java.util.ArrayList;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.protocol.packets.world.SleepClock;
import com.hypixel.hytale.builtin.beds.sleep.resources.WorldSleep;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.protocol.packets.world.SleepMultiplayer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.builtin.beds.sleep.systems.world.StartSlumberSystem;
import java.time.temporal.Temporal;
import java.time.Instant;
import com.hypixel.hytale.builtin.beds.sleep.systems.world.CanSleepInWorld;
import com.hypixel.hytale.builtin.beds.sleep.components.PlayerSleep;
import java.util.Objects;
import com.hypixel.hytale.builtin.beds.sleep.resources.WorldSlumber;
import com.hypixel.hytale.builtin.beds.sleep.components.PlayerSomnolence;
import com.hypixel.hytale.builtin.beds.sleep.resources.WorldSomnolence;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.builtin.beds.sleep.components.SleepTracker;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.protocol.packets.world.UpdateSleepState;
import java.util.UUID;
import java.time.Duration;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.tick.DelayedEntitySystem;

public class UpdateSleepPacketSystem extends DelayedEntitySystem<EntityStore>
{
    public static final Query<EntityStore> QUERY;
    public static final Duration SPAN_BEFORE_BLACK_SCREEN;
    public static final int MAX_SAMPLE_COUNT = 5;
    private static final UUID[] EMPTY_UUIDS;
    private static final UpdateSleepState PACKET_NO_SLEEP_UI;
    
    @Override
    public Query<EntityStore> getQuery() {
        return UpdateSleepPacketSystem.QUERY;
    }
    
    public UpdateSleepPacketSystem() {
        super(0.25f);
    }
    
    @Override
    public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        UpdateSleepState packet = this.createSleepPacket(store, index, archetypeChunk);
        final SleepTracker sleepTrackerComponent = archetypeChunk.getComponent(index, SleepTracker.getComponentType());
        assert sleepTrackerComponent != null;
        packet = sleepTrackerComponent.generatePacketToSend(packet);
        if (packet != null) {
            final PlayerRef playerRefComponent = archetypeChunk.getComponent(index, PlayerRef.getComponentType());
            assert playerRefComponent != null;
            playerRefComponent.getPacketHandler().write(packet);
        }
    }
    
    private UpdateSleepState createSleepPacket(@Nonnull final Store<EntityStore> store, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk) {
        final World world = store.getExternalData().getWorld();
        final WorldSomnolence worldSomnolence = store.getResource(WorldSomnolence.getResourceType());
        final WorldSleep worldSleepState = worldSomnolence.getState();
        final PlayerSomnolence playerSomnolenceComponent = archetypeChunk.getComponent(index, PlayerSomnolence.getComponentType());
        assert playerSomnolenceComponent != null;
        final PlayerSleep playerSleepState = playerSomnolenceComponent.getSleepState();
        SleepClock sleepClock;
        if (worldSleepState instanceof final WorldSlumber slumber) {
            sleepClock = slumber.createSleepClock();
        }
        else {
            sleepClock = null;
        }
        final SleepClock clock = sleepClock;
        final PlayerSleep obj = playerSleepState;
        Objects.requireNonNull(obj);
        final PlayerSleep playerSleep = obj;
        return switch (/* invokedynamic(!) */ProcyonInvokeDynamicHelper_25.invoke(playerSleep, false)) {
            default -> throw new MatchException(null, null);
            case 0 -> {
                final PlayerSleep.FullyAwake ignored = (PlayerSleep.FullyAwake)playerSleep;
                yield UpdateSleepPacketSystem.PACKET_NO_SLEEP_UI;
            }
            case 1 -> {
                final PlayerSleep.MorningWakeUp ignored2 = (PlayerSleep.MorningWakeUp)playerSleep;
                yield UpdateSleepPacketSystem.PACKET_NO_SLEEP_UI;
            }
            case 2 -> {
                final PlayerSleep.NoddingOff noddingOff = (PlayerSleep.NoddingOff)playerSleep;
                if (CanSleepInWorld.check(world).isNegative()) {
                    yield UpdateSleepPacketSystem.PACKET_NO_SLEEP_UI;
                }
                final long elapsedMs = Duration.between(noddingOff.realTimeStart(), Instant.now()).toMillis();
                final boolean grayFade = elapsedMs > UpdateSleepPacketSystem.SPAN_BEFORE_BLACK_SCREEN.toMillis();
                final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
                final boolean readyToSleep = StartSlumberSystem.isReadyToSleep(store, ref);
                yield new UpdateSleepState(grayFade, false, clock, readyToSleep ? this.createSleepMultiplayer(store) : null);
            }
            case 3 -> {
                final PlayerSleep.Slumber ignored3 = (PlayerSleep.Slumber)playerSleep;
                yield new UpdateSleepState(true, true, clock, null);
            }
        };
    }
    
    @Nullable
    private SleepMultiplayer createSleepMultiplayer(@Nonnull final Store<EntityStore> store) {
        final World world = store.getExternalData().getWorld();
        final List<PlayerRef> playerRefs = new ArrayList<PlayerRef>(world.getPlayerRefs());
        PlayerRef playerRef = null;
        playerRefs.removeIf(playerRef -> playerRef.getReference() == null);
        if (playerRefs.size() <= 1) {
            return null;
        }
        Ref<EntityStore> ref = null;
        playerRefs.sort(Comparator.comparingLong(ref -> ref.getUuid().hashCode() + world.hashCode()));
        int sleepersCount = 0;
        int awakeCount = 0;
        final List<UUID> awakeSampleList = new ArrayList<UUID>(playerRefs.size());
        final Iterator<PlayerRef> iterator = playerRefs.iterator();
        while (iterator.hasNext()) {
            playerRef = iterator.next();
            ref = playerRef.getReference();
            final boolean readyToSleep = StartSlumberSystem.isReadyToSleep(store, ref);
            if (readyToSleep) {
                ++sleepersCount;
            }
            else {
                ++awakeCount;
                awakeSampleList.add(playerRef.getUuid());
            }
        }
        final UUID[] awakeSample = (awakeSampleList.size() > 5) ? UpdateSleepPacketSystem.EMPTY_UUIDS : awakeSampleList.toArray(UUID[]::new);
        return new SleepMultiplayer(sleepersCount, awakeCount, awakeSample);
    }
    
    static {
        QUERY = Query.and(PlayerRef.getComponentType(), PlayerSomnolence.getComponentType(), SleepTracker.getComponentType());
        SPAN_BEFORE_BLACK_SCREEN = Duration.ofMillis(1200L);
        EMPTY_UUIDS = new UUID[0];
        PACKET_NO_SLEEP_UI = new UpdateSleepState(false, false, null, null);
    }
    
    // This helper class was generated by Procyon to approximate the behavior of an
    // 'invokedynamic' instruction that it doesn't know how to interpret.
    private static final class ProcyonInvokeDynamicHelper_25
    {
        private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
        private static MethodHandle handle;
        private static volatile int fence;
        
        private static MethodHandle handle() {
            final MethodHandle handle = ProcyonInvokeDynamicHelper_25.handle;
            if (handle != null)
                return handle;
            return ProcyonInvokeDynamicHelper_25.ensureHandle();
        }
        
        private static MethodHandle ensureHandle() {
            ProcyonInvokeDynamicHelper_25.fence = 0;
            MethodHandle handle = ProcyonInvokeDynamicHelper_25.handle;
            if (handle == null) {
                MethodHandles.Lookup lookup = ProcyonInvokeDynamicHelper_25.LOOKUP;
                try {
                    handle = ((CallSite)SwitchBootstraps.typeSwitch(lookup, "typeSwitch", MethodType.methodType(int.class, Object.class, int.class), PlayerSleep.FullyAwake.class, PlayerSleep.MorningWakeUp.class, PlayerSleep.NoddingOff.class, PlayerSleep.Slumber.class)).dynamicInvoker();
                }
                catch (Throwable t) {
                    throw new UndeclaredThrowableException(t);
                }
                ProcyonInvokeDynamicHelper_25.fence = 1;
                ProcyonInvokeDynamicHelper_25.handle = handle;
                ProcyonInvokeDynamicHelper_25.fence = 0;
            }
            return handle;
        }
        
        private static int invoke(Object p0, int p1) {
            try {
                return ProcyonInvokeDynamicHelper_25.handle().invokeExact(p0, p1);
            }
            catch (Throwable t) {
                throw new UndeclaredThrowableException(t);
            }
        }
    }
}
