// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0

package dan200.computercraft.client.platform;

import dan200.computercraft.client.ClientTableFormatter;
import dan200.computercraft.client.gui.AbstractComputerScreen;
import dan200.computercraft.client.gui.OptionScreen;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.command.text.TableBuilder;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.JukeboxSong;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.LevelEvent;
import org.jspecify.annotations.Nullable;

import java.util.UUID;

/**
 * The client-side implementation of {@link ClientNetworkContext}.
 */
public final class ClientNetworkContextImpl implements ClientNetworkContext {
    @Override
    public void handleChatTable(TableBuilder table) {
        ClientTableFormatter.INSTANCE.display(table);
    }

    @Override
    public void handleComputerTerminal(int containerId, TerminalState terminal) {
        Player player = Minecraft.getInstance().player;
        if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) {
            menu.updateTerminal(terminal);
        }
    }

    @Override
    public void handleMonitorData(BlockPos pos, @Nullable TerminalState terminal) {
        var player = Minecraft.getInstance().player;
        if (player == null) return;

        var te = player.level().getBlockEntity(pos);
        if (!(te instanceof MonitorBlockEntity monitor)) return;

        monitor.read(terminal);
    }

    @Override
    public void handlePlayRecord(BlockPos pos, @Nullable Holder<JukeboxSong> song) {
        var level = Minecraft.getInstance().level;
        if (level == null) return;

        if (song == null) {
            level.levelEvent(LevelEvent.SOUND_STOP_JUKEBOX_SONG, pos, 0);
        } else {
            var id = level.registryAccess().lookupOrThrow(Registries.JUKEBOX_SONG).getIdOrThrow(song.value());
            level.levelEvent(LevelEvent.SOUND_PLAY_JUKEBOX_SONG, pos, id);
        }
    }

    @Override
    public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, @Nullable TerminalState terminal) {
        ClientPocketComputers.setState(instanceId, state, lightState, terminal);
    }

    @Override
    public void handlePocketComputerDeleted(UUID instanceId) {
        ClientPocketComputers.remove(instanceId);
    }

    @Override
    public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio buffer) {
        SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
    }

    @Override
    public void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
        SpeakerManager.moveSound(source, reifyPosition(position));
    }

    @Override
    public void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, Identifier sound, float volume, float pitch) {
        SpeakerManager.getSound(source).playSound(reifyPosition(position), sound, volume, pitch);
    }

    @Override
    public void handleSpeakerStop(UUID source) {
        SpeakerManager.stopSound(source);
    }

    @Override
    public void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) {
        var minecraft = Minecraft.getInstance();

        var screen = OptionScreen.unwrap(minecraft.gui.screen());
        if (screen instanceof AbstractComputerScreen<?> && ((AbstractComputerScreen<?>) screen).getMenu().containerId == containerId) {
            ((AbstractComputerScreen<?>) screen).uploadResult(result, errorMessage);
        }
    }

    private static SpeakerPosition reifyPosition(SpeakerPosition.Message pos) {
        var minecraft = Minecraft.getInstance();
        Level level = minecraft.level;
        if (level != null && !level.dimension().identifier().equals(pos.level())) level = null;

        return new SpeakerPosition(
            level, pos.position(),
            level != null && pos.entity().isPresent() ? level.getEntity(pos.entity().getAsInt()) : null
        );
    }
}
