package org.bukkit.craftbukkit.inventory;

import net.minecraft.server.ChatComponentText;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;

import net.minecraft.server.Container;
import net.minecraft.server.EntityHuman;
import net.minecraft.server.IInventory;
import net.minecraft.server.PacketPlayOutOpenWindow;
import net.minecraft.server.Slot;
import net.minecraft.server.SlotShulkerBox;

public class CraftContainer extends Container {

    private final InventoryView view;
    private InventoryType cachedType;
    private String cachedTitle;
    private final int cachedSize;

    public CraftContainer(InventoryView view, int id) {
        this.view = view;
        this.windowId = id;
        // TODO: Do we need to check that it really is a CraftInventory?
        IInventory top = ((CraftInventory) view.getTopInventory()).getInventory();
        IInventory bottom = ((CraftInventory) view.getBottomInventory()).getInventory();
        cachedType = view.getType();
        cachedTitle = view.getTitle();
        cachedSize = getSize();
        setupSlots(top, bottom);
    }

    public CraftContainer(final Inventory inventory, final HumanEntity player, int id) {
        this(new InventoryView() {
            @Override
            public Inventory getTopInventory() {
                return inventory;
            }

            @Override
            public Inventory getBottomInventory() {
                return player.getInventory();
            }

            @Override
            public HumanEntity getPlayer() {
                return player;
            }

            @Override
            public InventoryType getType() {
                return inventory.getType();
            }
        }, id);
    }

    @Override
    public InventoryView getBukkitView() {
        return view;
    }

    private int getSize() {
        return view.getTopInventory().getSize();
    }

    @Override
    public boolean c(EntityHuman entityhuman) {
        if (cachedType == view.getType() && cachedSize == getSize() && cachedTitle.equals(view.getTitle())) {
            return true;
        }
        // If the window type has changed for some reason, update the player
        // This method will be called every tick or something, so it's
        // as good a place as any to put something like this.
        boolean typeChanged = (cachedType != view.getType());
        cachedType = view.getType();
        cachedTitle = view.getTitle();
        if (view.getPlayer() instanceof CraftPlayer) {
            CraftPlayer player = (CraftPlayer) view.getPlayer();
            String type = getNotchInventoryType(cachedType);
            IInventory top = ((CraftInventory) view.getTopInventory()).getInventory();
            IInventory bottom = ((CraftInventory) view.getBottomInventory()).getInventory();
            this.b.clear();
            this.c.clear();
            if (typeChanged) {
                setupSlots(top, bottom);
            }
            int size = getSize();
            player.getHandle().playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.windowId, type, new ChatComponentText(cachedTitle), size));
            player.updateInventory();
        }
        return true;
    }

    public static String getNotchInventoryType(InventoryType type) {
        switch (type) {
            case WORKBENCH:
                return "minecraft:crafting_table";
            case FURNACE:
                return "minecraft:furnace";
            case DISPENSER:
                return "minecraft:dispenser";
            case ENCHANTING:
                return "minecraft:enchanting_table";
            case BREWING:
                return "minecraft:brewing_stand";
            case BEACON:
                return "minecraft:beacon";
            case ANVIL:
                return "minecraft:anvil";
            case HOPPER:
                return "minecraft:hopper";
            case DROPPER:
                return "minecraft:dropper";
            case SHULKER_BOX:
                return "minecraft:shulker_box";
            default:
                return "minecraft:chest";
        }
    }

    private void setupSlots(IInventory top, IInventory bottom) {
        switch (cachedType) {
            case CREATIVE:
                break; // TODO: This should be an error?
            case PLAYER:
            case CHEST:
                setupChest(top, bottom);
                break;
            case DISPENSER:
            case DROPPER:
                setupDispenser(top, bottom);
                break;
            case FURNACE:
                setupFurnace(top, bottom);
                break;
            case CRAFTING: // TODO: This should be an error?
            case WORKBENCH:
                setupWorkbench(top, bottom);
                break;
            case ENCHANTING:
                setupEnchanting(top, bottom);
                break;
            case BREWING:
                setupBrewing(top, bottom);
                break;
            case HOPPER:
                setupHopper(top, bottom);
                break;
            case ANVIL:
                setupAnvil(top, bottom);
                break;
            case BEACON:
                setupBeacon(top, bottom);
                break;
            case SHULKER_BOX:
                setupShulkerBox(top, bottom);
                break;
        }
    }

    private void setupChest(IInventory top, IInventory bottom) {
        int rows = top.getSize() / 9;
        int row;
        int col;
        // This code copied from ContainerChest
        int i = (rows - 4) * 18;
        for (row = 0; row < rows; ++row) {
            for (col = 0; col < 9; ++col) {
                this.a(new Slot(top, col + row * 9, 8 + col * 18, 18 + row * 18));
            }
        }

        for (row = 0; row < 3; ++row) {
            for (col = 0; col < 9; ++col) {
                this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 103 + row * 18 + i));
            }
        }

        for (col = 0; col < 9; ++col) {
            this.a(new Slot(bottom, col, 8 + col * 18, 161 + i));
        }
        // End copy from ContainerChest
    }

    private void setupWorkbench(IInventory top, IInventory bottom) {
        // This code copied from ContainerWorkbench
        this.a(new Slot(top, 0, 124, 35));

        int row;
        int col;

        for (row = 0; row < 3; ++row) {
            for (col = 0; col < 3; ++col) {
                this.a(new Slot(top, 1 + col + row * 3, 30 + col * 18, 17 + row * 18));
            }
        }

        for (row = 0; row < 3; ++row) {
            for (col = 0; col < 9; ++col) {
                this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18));
            }
        }

        for (col = 0; col < 9; ++col) {
            this.a(new Slot(bottom, col, 8 + col * 18, 142));
        }
        // End copy from ContainerWorkbench
    }

    private void setupFurnace(IInventory top, IInventory bottom) {
        // This code copied from ContainerFurnace
        this.a(new Slot(top, 0, 56, 17));
        this.a(new Slot(top, 1, 56, 53));
        this.a(new Slot(top, 2, 116, 35));

        int row;
        int col;

        for (row = 0; row < 3; ++row) {
            for (col = 0; col < 9; ++col) {
                this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18));
            }
        }

        for (col = 0; col < 9; ++col) {
            this.a(new Slot(bottom, col, 8 + col * 18, 142));
        }
        // End copy from ContainerFurnace
    }

    private void setupDispenser(IInventory top, IInventory bottom) {
        // This code copied from ContainerDispenser
        int row;
        int col;

        for (row = 0; row < 3; ++row) {
            for (col = 0; col < 3; ++col) {
                this.a(new Slot(top, col + row * 3, 61 + col * 18, 17 + row * 18));
            }
        }

        for (row = 0; row < 3; ++row) {
            for (col = 0; col < 9; ++col) {
                this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18));
            }
        }

        for (col = 0; col < 9; ++col) {
            this.a(new Slot(bottom, col, 8 + col * 18, 142));
        }
        // End copy from ContainerDispenser
    }

    private void setupEnchanting(IInventory top, IInventory bottom) {
        // This code copied from ContainerEnchantTable
        this.a((new Slot(top, 0, 15, 47)));
        this.a((new Slot(top, 0, 35, 47)));

        int row;

        for (row = 0; row < 3; ++row) {
            for (int i1 = 0; i1 < 9; ++i1) {
                this.a(new Slot(bottom, i1 + row * 9 + 9, 8 + i1 * 18, 84 + row * 18));
            }
        }

        for (row = 0; row < 9; ++row) {
            this.a(new Slot(bottom, row, 8 + row * 18, 142));
        }
        // End copy from ContainerEnchantTable
    }

    private void setupBrewing(IInventory top, IInventory bottom) {
        // This code copied from ContainerBrewingStand
        this.a(new Slot(top, 0, 56, 46));
        this.a(new Slot(top, 1, 79, 53));
        this.a(new Slot(top, 2, 102, 46));
        this.a(new Slot(top, 3, 79, 17));
        this.a(new Slot(top, 4, 17, 17));

        int i;
        for (i = 0; i < 3; ++i) {
            for (int j = 0; j < 9; ++j) {
                this.a(new Slot(bottom, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
            }
        }

        for (i = 0; i < 9; ++i) {
            this.a(new Slot(bottom, i, 8 + i * 18, 142));
        }
        // End copy from ContainerBrewingStand
    }

    private void setupHopper(IInventory top, IInventory bottom) {
        // This code copied from ContainerHopper
        byte b0 = 51;

        int i;

        for (i = 0; i < top.getSize(); ++i) {
            this.a(new Slot(top, i, 44 + i * 18, 20));
        }

        for (i = 0; i < 3; ++i) {
            for (int j = 0; j < 9; ++j) {
                this.a(new Slot(bottom, j + i * 9 + 9, 8 + j * 18, i * 18 + b0));
            }
        }

        for (i = 0; i < 9; ++i) {
            this.a(new Slot(bottom, i, 8 + i * 18, 58 + b0));
        }
        // End copy from ContainerHopper
    }

    private void setupAnvil(IInventory top, IInventory bottom) {
        // This code copied from ContainerAnvil
        this.a(new Slot(top, 0, 27, 47));
        this.a(new Slot(top, 1, 76, 47));
        this.a(new Slot(top, 2, 134, 47));

        int i;

        for (i = 0; i < 3; ++i) {
            for (int j = 0; j < 9; ++j) {
                this.a(new Slot(bottom, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
            }
        }

        for (i = 0; i < 9; ++i) {
            this.a(new Slot(bottom, i, 8 + i * 18, 142));
        }
        // End copy from ContainerAnvil
    }

    private void setupBeacon(IInventory top, IInventory bottom) {
        // This code is copied from ContainerBeacon
        this.a(new Slot(top, 0, 136, 110));

        int i;

        for (i = 0; i < 3; ++i) {
            for (int j = 0; j < 9; ++j) {
                this.a(new Slot(bottom, j + i * 9 + 9, 36 + j * 18, 137 + i * 18));
            }
        }

        for (i = 0; i < 9; ++i) {
            this.a(new Slot(bottom, i, 36 + i * 18, 195));
        }
        // End copy from ContainerBeacon
    }

    private void setupShulkerBox(IInventory top, IInventory bottom) {
        // This code is copied from ContainerShulkerBox
        int i;
        int j;

        for (i = 0; i < 3; ++i) {
            for (j = 0; j < 9; ++j) {
                this.a((Slot) (new SlotShulkerBox(top, j + i * 9, 8 + j * 18, 18 + i * 18)));
            }
        }

        for (i = 0; i < 3; ++i) {
            for (j = 0; j < 9; ++j) {
                this.a(new Slot(bottom, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
            }
        }

        for (i = 0; i < 9; ++i) {
            this.a(new Slot(bottom, i, 8 + i * 18, 142));
        }
        // End copy from ContainerShulkerBox
    }

    public boolean a(EntityHuman entity) {
        return true;
    }
}
