package com.nukkitx.protocol.bedrock.v361.serializer;

import com.nukkitx.network.VarInts;
import com.nukkitx.protocol.bedrock.data.CraftingData;
import com.nukkitx.protocol.bedrock.data.CraftingType;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
import com.nukkitx.protocol.bedrock.v361.BedrockUtils;
import com.nukkitx.protocol.serializer.PacketSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.util.UUID;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CraftingDataSerializer_v361 implements PacketSerializer<CraftingDataPacket> {
    public static final CraftingDataSerializer_v361 INSTANCE = new CraftingDataSerializer_v361();

    private static final InternalLogger log = InternalLoggerFactory.getInstance(CraftingDataSerializer_v361.class);

    @Override
    public void serialize(ByteBuf buffer, CraftingDataPacket packet) {
        BedrockUtils.writeArray(buffer, packet.getCraftingData(), (buf, craftingData) -> {
            VarInts.writeInt(buf, craftingData.getType().ordinal());
            switch (craftingData.getType()) {
                case SHAPELESS:
                case SHAPELESS_CHEMISTRY:
                case SHULKER_BOX:
                    BedrockUtils.writeString(buf, craftingData.getRecipeId());
                    BedrockUtils.writeArray(buf, craftingData.getInputs(), BedrockUtils::writeRecipeIngredient);
                    BedrockUtils.writeArray(buf, craftingData.getOutputs(), BedrockUtils::writeItemData);
                    BedrockUtils.writeUuid(buf, craftingData.getUuid());
                    BedrockUtils.writeString(buf, craftingData.getCraftingTag());
                    VarInts.writeInt(buf, craftingData.getPriority());
                    break;
                case SHAPED:
                case SHAPED_CHEMISTRY:
                    BedrockUtils.writeString(buf, craftingData.getRecipeId());
                    VarInts.writeInt(buf, craftingData.getWidth());
                    VarInts.writeInt(buf, craftingData.getHeight());
                    int count = craftingData.getWidth() * craftingData.getHeight();
                    ItemData[] inputs = craftingData.getInputs();
                    for (int i = 0; i < count; i++) {
                        BedrockUtils.writeRecipeIngredient(buf, inputs[i]);
                    }
                    BedrockUtils.writeArray(buf, craftingData.getOutputs(), BedrockUtils::writeItemData);
                    BedrockUtils.writeUuid(buf, craftingData.getUuid());
                    BedrockUtils.writeString(buf, craftingData.getCraftingTag());
                    VarInts.writeInt(buf, craftingData.getPriority());
                    break;
                case FURNACE:
                case FURNACE_DATA:
                    VarInts.writeInt(buf, craftingData.getInputId());
                    if (craftingData.getType() == CraftingType.FURNACE_DATA) {
                        VarInts.writeInt(buf, craftingData.getInputDamage());
                    }
                    BedrockUtils.writeItemData(buf, craftingData.getOutputs()[0]);
                    BedrockUtils.writeString(buf, craftingData.getCraftingTag());
                    break;
                case MULTI:
                    BedrockUtils.writeUuid(buf, craftingData.getUuid());
                    break;
            }
        });
        buffer.writeBoolean(packet.isCleanRecipes());
    }

    @Override
    public void deserialize(ByteBuf buffer, CraftingDataPacket packet) {
        BedrockUtils.readArray(buffer, packet.getCraftingData(), buf -> {
            int typeInt = VarInts.readInt(buf);
            CraftingType type = CraftingType.byId(typeInt);
            if (type == null) {
                throw new IllegalArgumentException("Unknown crafting type: " + typeInt);
            }

            switch (type) {
                case SHAPELESS:
                case SHAPELESS_CHEMISTRY:
                case SHULKER_BOX:
                    String recipeId = BedrockUtils.readString(buf);
                    int inputCount = VarInts.readUnsignedInt(buf);
                    ItemData[] inputs = new ItemData[inputCount];
                    for (int i = 0; i < inputCount; i++) {
                        inputs[i] = BedrockUtils.readRecipeIngredient(buf);
                    }
                    int outputCount = VarInts.readUnsignedInt(buf);
                    ItemData[] outputs = new ItemData[outputCount];
                    for (int i = 0; i < outputCount; i++) {
                        outputs[i] = BedrockUtils.readItemData(buf);
                    }
                    UUID uuid = BedrockUtils.readUuid(buf);
                    String craftingTag = BedrockUtils.readString(buf);
                    int priority = VarInts.readInt(buf);
                    return new CraftingData(type, recipeId, -1, -1, -1, -1, inputs,
                            outputs, uuid, craftingTag, priority);
                case SHAPED:
                case SHAPED_CHEMISTRY:
                    recipeId = BedrockUtils.readString(buf);
                    int width = VarInts.readInt(buf);
                    int height = VarInts.readInt(buf);
                    inputCount = width * height;
                    inputs = new ItemData[inputCount];
                    for (int i = 0; i < inputCount; i++) {
                        inputs[i] = BedrockUtils.readRecipeIngredient(buf);
                    }
                    outputCount = VarInts.readUnsignedInt(buf);
                    outputs = new ItemData[outputCount];
                    for (int i = 0; i < outputCount; i++) {
                        outputs[i] = BedrockUtils.readItemData(buf);
                    }
                    uuid = BedrockUtils.readUuid(buf);
                    craftingTag = BedrockUtils.readString(buf);
                    priority = VarInts.readInt(buf);
                    return new CraftingData(type, recipeId, width, height, -1, -1, inputs, outputs,
                            uuid, craftingTag, priority);
                case FURNACE:
                case FURNACE_DATA:
                    int inputId = VarInts.readInt(buf);
                    int inputDamage = type == CraftingType.FURNACE_DATA ? VarInts.readInt(buf) : -1;
                    ItemData[] output = new ItemData[]{BedrockUtils.readItemData(buf)};
                    craftingTag = BedrockUtils.readString(buf);
                    return new CraftingData(type, null, -1, -1, inputId, inputDamage, null,
                            output, null, craftingTag, -1);
                case MULTI:
                    uuid = BedrockUtils.readUuid(buf);
                    return CraftingData.fromMulti(uuid);
                default:
                    throw new IllegalArgumentException("Unhandled crafting data type: " + type);
            }
        });
        packet.setCleanRecipes(buffer.readBoolean());
    }
}