/*
 * Copyright (c) 2019-2026 GeyserMC. http://geysermc.org
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @author GeyserMC
 * @link https://github.com/GeyserMC/Geyser
 */

package org.geysermc.geyser.translator.protocol.bedrock;

import org.cloudburstmc.protocol.bedrock.packet.LecternUpdatePacket;
import org.geysermc.geyser.inventory.InventoryHolder;
import org.geysermc.geyser.inventory.LecternContainer;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;

/**
 * Used to translate moving pages, or closing the inventory
 */
@Translator(packet = LecternUpdatePacket.class)
public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpdatePacket> {

    @Override
    public void translate(GeyserSession session, LecternUpdatePacket packet) {
        // Bedrock wants to either move a page or exit
        InventoryHolder<?> holder = session.getInventoryHolder();
        if (holder == null || !(holder.inventory() instanceof LecternContainer lecternContainer)) {
            session.getGeyser().getLogger().debug("Expected lectern but it wasn't open!");
            return;
        }

        // Books on java can only be 100 pages so clamp the page number to that
        // This also forces requested pages to be sequential
        // This is 25 for showing page 49/50, and 50 for 99/100
        int currentPage = lecternContainer.getCurrentBedrockPage();
        int requestedPage = packet.getPage();
        int page = MathUtils.constrain(MathUtils.constrain(requestedPage, currentPage - 1, currentPage + 1), 0, lecternContainer.getMaxPages());

        if (currentPage == requestedPage) {
            // The same page means Bedrock is closing the window
            InventoryUtils.sendJavaContainerClose(holder);
            InventoryUtils.closeInventory(session, holder, false);
        } else if (currentPage == page) {
            // The requested page was clamped back to the current page so do nothing
        } else {
            // Each "page" Bedrock gives to us actually represents two pages (think opening a book and seeing two pages)
            // Each "page" on Java is just one page (think a spiral notebook folded back to only show one page)
            int newJavaPage = (page * 2);
            int currentJavaPage = (currentPage * 2);

            // So, fun fact: We need to separately handle fake lecterns!
            // Since those are not actually a real lectern... the Java server won't respond to our requests.
            if (!lecternContainer.isUsingRealBlock()) {
                holder.updateProperty(0, newJavaPage);
                return;
            }

            // Send as many click button packets as we need to
            // Java has the option to specify exact page numbers by adding 100 to the number, but buttonId variable
            // is a byte when transmitted over the network and therefore this stops us at 128
            if (newJavaPage > currentJavaPage) {
                for (int i = currentJavaPage; i < newJavaPage; i++) {
                    ServerboundContainerButtonClickPacket clickButtonPacket = new ServerboundContainerButtonClickPacket(lecternContainer.getJavaId(), 2);
                    session.sendDownstreamGamePacket(clickButtonPacket);
                }
            } else {
                for (int i = currentJavaPage; i > newJavaPage; i--) {
                    ServerboundContainerButtonClickPacket clickButtonPacket = new ServerboundContainerButtonClickPacket(lecternContainer.getJavaId(), 1);
                    session.sendDownstreamGamePacket(clickButtonPacket);
                }
            }
        }
    }
}
