/*
 * This file is part of Sponge, licensed under the MIT License (MIT).
 *
 * Copyright (c) SpongePowered <https://www.spongepowered.org>
 * Copyright (c) contributors
 *
 * 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.
 */
package org.spongepowered.common.item.inventory.lens.impl.comp;

import org.spongepowered.api.item.inventory.InventoryProperty;

import org.spongepowered.api.data.Property.Operator;
import static com.google.common.base.Preconditions.*;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import org.spongepowered.api.item.inventory.Inventory;
import org.spongepowered.api.item.inventory.property.SlotIndex;
import org.spongepowered.api.item.inventory.property.SlotPos;
import org.spongepowered.common.item.inventory.adapter.InventoryAdapter;
import org.spongepowered.common.item.inventory.adapter.impl.comp.Inventory2DAdapter;
import org.spongepowered.common.item.inventory.lens.Fabric;
import org.spongepowered.common.item.inventory.lens.SlotProvider;
import org.spongepowered.common.item.inventory.lens.comp.Inventory2DLens;
import org.spongepowered.common.item.inventory.lens.impl.struct.LensHandle;
import org.spongepowered.common.item.inventory.lens.slots.SlotLens;

public class Inventory2DLensImpl extends OrderedInventoryLensImpl implements Inventory2DLens<IInventory, net.minecraft.item.ItemStack> {
    
    protected final int width;
    protected final int height;
    
    protected final int xBase;
    protected final int yBase;

    public Inventory2DLensImpl(int base, int width, int height, SlotProvider<IInventory, ItemStack> slots) {
        this(base, width, height, width, Inventory2DAdapter.class, slots);
    }

    public Inventory2DLensImpl(int base, int width, int height, Class<? extends Inventory> adapterType, SlotProvider<IInventory, ItemStack> slots) {
        this(base, width, height, width, adapterType, slots);
    }
    
    public Inventory2DLensImpl(int base, int width, int height, int rowStride, Class<? extends Inventory> adapterType, SlotProvider<IInventory, ItemStack> slots) {
        this(base, width, height, rowStride, 0, 0, adapterType, slots);
    }
    
    protected Inventory2DLensImpl(int base, int width, int height, int rowStride, int xBase, int yBase, Class<? extends Inventory> adapterType, SlotProvider<IInventory, ItemStack> slots) {
        super(base, width * height, rowStride, adapterType, slots);
        
        checkArgument(width > 0, "Invalid width: %s", width);
        checkArgument(height > 0, "Invalid height: %s", height);
        checkArgument(rowStride >= width, "Invalid stride: stride=%s, width=%s", rowStride, width);
        
        this.width = width;
        this.height = height;
        this.xBase = xBase;
        this.yBase = yBase;
        
        this.init(null, slots);
    }
    
    @Override
    protected boolean isDelayedInit() {
        return true;
    }
    
    @Override
    protected void init(SlotProvider<IInventory, ItemStack> slots) {
        this.init(slots, true);
    }

    /**
     * Basic initialiser for two dimensional inventories. Adds child slots
     * directly to this lens starting at {@link #base} and creating a
     * rectangular array of slots {@link #width} by {@link #height} assuming
     * that the target inventory is {@link #stride} slots wide.
     * 
     * @param slots
     * @param spanning Set to true to create spanning slots, false to create
     *      normal child slots
     */
    protected void init(SlotProvider<IInventory, ItemStack> slots, boolean spanning) {
        for (int y = 0, ord = 0, slot = this.base; y < this.height; y++, slot += (this.stride - this.width)) {
            for (int x = 0; x < this.width; x++, slot++, ord++) {
                SlotLens<IInventory, ItemStack> slotLens = slots.getSlot(slot);
//                System.err.printf(">> %s ord: %-4d x: %-4d y: %-4d slot: %-4d => %s\n",
//                        this.getClass().getSimpleName(), ord, x, y, slot, slotLens.getClass().getSimpleName());
                if (spanning) {
                    this.addSpanningChild(slotLens, new SlotIndex(ord), new SlotPos(this.xBase + x, this.yBase + y));
                } else {
                    this.addChild(slotLens, new SlotIndex(ord), new SlotPos(this.xBase + x, this.yBase + y));
                }
            }
        }
    }

    @Override
    public int getWidth() {
        return this.width;
    }

    @Override
    public int getHeight() {
        return this.height;
    }

    @Override
    public SlotLens<IInventory, ItemStack> getSlot(SlotPos pos) {
        if (pos.getOperator() != Operator.EQUAL) {
            return null;
        }
        
        for (LensHandle<IInventory, ItemStack> slot : this.slotCache) {
            InventoryProperty<Object, Object> slotPos = slot.getProperty(SlotPos.class);
            if (slotPos != null && pos.matches(slotPos)) {
                return (SlotLens<IInventory, ItemStack>) slot.lens;
            }
        }
        
        return null;
    }

    @Override
    public int getRealIndex(Fabric<IInventory> inv, int ordinal) {
        if (!this.checkOrdinal(ordinal)) {
            return -1;
        }

        return this.base + ((ordinal / this.width) * this.stride) + (ordinal % this.width);
    }
    
    @Override
    public InventoryAdapter<IInventory, ItemStack> getAdapter(Fabric<IInventory> inv, Inventory parent) {
        return new Inventory2DAdapter(inv, this, parent);
    }

}

@@ -1310,72 +1310,8 @@
 p;%0A%0A
-import org.spongepowered.api.item.inventory.InventoryProperty;%0A%0A
 impo
@@ -2036,84 +2036,8 @@
 ns;%0A
-import org.spongepowered.common.item.inventory.lens.impl.struct.LensHandle;%0A
 impo
@@ -5546,32 +5546,24 @@
 ;%0A        %7D%0A
-        
 %0A        for
@@ -5563,23 +5563,24 @@
     
-for (LensHandle
+return (SlotLens
 %3CIIn
@@ -5602,302 +5602,72 @@
 ack%3E
- slot : this.slotCache) %7B%0A            InventoryProperty%3CObject, Object%3E slotPos = slot.getProperty(SlotPos.class);%0A            if (slotPos != null && pos.matches(slotPos)) %7B%0A                return (SlotLens%3CIInventory, ItemStack%3E) slot.lens;%0A            %7D%0A        %7D%0A        %0A        return null
+) this.spanningChildren.get(pos.getY()).lens.getLens(pos.getX())
 ;%0A  

Fix Inventory2DLensImpl#getSlot always returning null