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

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

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 com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import java.time.temporal.TemporalAmount;
import java.util.Objects;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.time.temporal.Temporal;
import java.time.chrono.ChronoLocalDateTime;
import java.time.ZoneId;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import com.hypixel.hytale.component.Ref;
import java.time.Instant;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.Collection;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.builtin.beds.sleep.components.PlayerSleep;
import com.hypixel.hytale.builtin.beds.sleep.components.PlayerSomnolence;
import com.hypixel.hytale.builtin.beds.sleep.resources.WorldSlumber;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.builtin.beds.sleep.resources.WorldSleep;
import com.hypixel.hytale.builtin.beds.sleep.resources.WorldSomnolence;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.Store;
import java.time.Duration;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.DelayedSystem;

public class StartSlumberSystem extends DelayedSystem<EntityStore>
{
    public static final Duration NODDING_OFF_DURATION;
    public static final Duration WAKE_UP_AUTOSLEEP_DELAY;
    
    public StartSlumberSystem() {
        super(0.3f);
    }
    
    @Override
    public void delayedTick(final float dt, final int systemIndex, @Nonnull final Store<EntityStore> store) {
        this.checkIfEveryoneIsReadyToSleep(store);
    }
    
    private void checkIfEveryoneIsReadyToSleep(final Store<EntityStore> store) {
        final World world = store.getExternalData().getWorld();
        final Collection<PlayerRef> playerRefs = world.getPlayerRefs();
        if (playerRefs.isEmpty()) {
            return;
        }
        if (CanSleepInWorld.check(world).isNegative()) {
            return;
        }
        final float wakeUpHour = world.getGameplayConfig().getWorldConfig().getSleepConfig().getWakeUpHour();
        final WorldSomnolence worldSomnolenceResource = store.getResource(WorldSomnolence.getResourceType());
        final WorldSleep worldState = worldSomnolenceResource.getState();
        if (worldState != WorldSleep.Awake.INSTANCE) {
            return;
        }
        if (this.isEveryoneReadyToSleep(store)) {
            final WorldTimeResource timeResource = store.getResource(WorldTimeResource.getResourceType());
            final Instant now = timeResource.getGameTime();
            final Instant target = this.computeWakeupInstant(now, wakeUpHour);
            final float irlSeconds = computeIrlSeconds(now, target);
            worldSomnolenceResource.setState(new WorldSlumber(now, target, irlSeconds));
            store.forEachEntityParallel(PlayerSomnolence.getComponentType(), (index, archetypeChunk, commandBuffer) -> {
                final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
                commandBuffer.putComponent(ref, PlayerSomnolence.getComponentType(), PlayerSleep.Slumber.createComponent(timeResource));
            });
        }
    }
    
    private Instant computeWakeupInstant(@Nonnull final Instant now, final float wakeUpHour) {
        final LocalDateTime ldt = LocalDateTime.ofInstant(now, ZoneOffset.UTC);
        final int hours = (int)wakeUpHour;
        final float fractionalHour = wakeUpHour - hours;
        LocalDateTime wakeUpTime = ldt.toLocalDate().atTime(hours, (int)(fractionalHour * 60.0f));
        if (!ldt.isBefore(wakeUpTime)) {
            wakeUpTime = wakeUpTime.plusDays(1L);
        }
        return wakeUpTime.toInstant(ZoneOffset.UTC);
    }
    
    private static float computeIrlSeconds(final Instant startInstant, final Instant targetInstant) {
        final long ms = Duration.between(startInstant, targetInstant).toMillis();
        final long hours = TimeUnit.MILLISECONDS.toHours(ms);
        final double seconds = Math.max(3.0, hours / 6.0);
        return (float)Math.ceil(seconds);
    }
    
    private boolean isEveryoneReadyToSleep(final ComponentAccessor<EntityStore> store) {
        final World world = store.getExternalData().getWorld();
        final Collection<PlayerRef> playerRefs = world.getPlayerRefs();
        if (playerRefs.isEmpty()) {
            return false;
        }
        for (final PlayerRef playerRef : playerRefs) {
            if (!isReadyToSleep(store, playerRef.getReference())) {
                return false;
            }
        }
        return true;
    }
    
    public static boolean isReadyToSleep(final ComponentAccessor<EntityStore> store, final Ref<EntityStore> ref) {
        final PlayerSomnolence somnolence = store.getComponent(ref, PlayerSomnolence.getComponentType());
        if (somnolence == null) {
            return false;
        }
        final PlayerSleep sleepState2;
        final PlayerSleep sleepState = sleepState2 = somnolence.getSleepState();
        Objects.requireNonNull(sleepState2);
        final PlayerSleep.Slumber slumber2 = (PlayerSleep.Slumber)sleepState2;
        return switch (/* invokedynamic(!) */ProcyonInvokeDynamicHelper_26.invoke(slumber2, false)) {
            default -> throw new MatchException(null, null);
            case 0 -> {
                final PlayerSleep.FullyAwake fullyAwake = (PlayerSleep.FullyAwake)slumber2;
                yield false;
            }
            case 1 -> {
                final PlayerSleep.MorningWakeUp morningWakeUp = (PlayerSleep.MorningWakeUp)slumber2;
                final WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType());
                final Instant readyTime = morningWakeUp.gameTimeStart().plus((TemporalAmount)StartSlumberSystem.WAKE_UP_AUTOSLEEP_DELAY);
                yield worldTimeResource.getGameTime().isAfter(readyTime);
            }
            case 2 -> {
                final PlayerSleep.NoddingOff noddingOff = (PlayerSleep.NoddingOff)slumber2;
                final Instant sleepStart = noddingOff.realTimeStart().plus((TemporalAmount)StartSlumberSystem.NODDING_OFF_DURATION);
                yield Instant.now().isAfter(sleepStart);
            }
            case 3 -> {
                final PlayerSleep.Slumber slumber = slumber2;
                yield true;
            }
        };
    }
    
    static {
        NODDING_OFF_DURATION = Duration.ofMillis(3200L);
        WAKE_UP_AUTOSLEEP_DELAY = Duration.ofHours(1L);
    }
    
    // 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_26
    {
        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_26.handle;
            if (handle != null)
                return handle;
            return ProcyonInvokeDynamicHelper_26.ensureHandle();
        }
        
        private static MethodHandle ensureHandle() {
            ProcyonInvokeDynamicHelper_26.fence = 0;
            MethodHandle handle = ProcyonInvokeDynamicHelper_26.handle;
            if (handle == null) {
                MethodHandles.Lookup lookup = ProcyonInvokeDynamicHelper_26.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_26.fence = 1;
                ProcyonInvokeDynamicHelper_26.handle = handle;
                ProcyonInvokeDynamicHelper_26.fence = 0;
            }
            return handle;
        }
        
        private static int invoke(Object p0, int p1) {
            try {
                return ProcyonInvokeDynamicHelper_26.handle().invokeExact(p0, p1);
            }
            catch (Throwable t) {
                throw new UndeclaredThrowableException(t);
            }
        }
    }
}
