package com.rs.game.item;

import java.io.Serializable;

/**
 * Container class.
 * 
 * @author Graham / edited by Dragonkk(Alex)
 * @param <T>
 */
public final class ItemsContainer<T extends Item> implements Serializable {

    private static final long serialVersionUID = 1099313426737026107L;

    private Item[] data;
    private boolean alwaysStackable;

    public ItemsContainer(int size, boolean alwaysStackable) {
	data = new Item[size];
	this.alwaysStackable = alwaysStackable;
    }

    public void shift() {
	Item[] oldData = data;
	data = new Item[oldData.length];
	int ptr = 0;
	for (int i = 0; i < data.length; i++) {
	    if (oldData[i] != null) {
		data[ptr++] = oldData[i];
	    }
	}
    }

    @SuppressWarnings("unchecked")
    public T get(int slot) {
	if (slot < 0 || slot >= data.length) {
	    return null;
	}
	return (T) data[slot];
    }

    public void set(int slot, T item) {
	if (slot < 0 || slot >= data.length) {
	    return;
	}
	data[slot] = item;
    }

    public void set2(int slot, Item item) {
	if (slot < 0 || slot >= data.length) {
	    return;
	}
	data[slot] = item;
    }

    public boolean forceAdd(T item) {
	for (int i = 0; i < data.length; i++) {
	    if (data[i] == null) {
		data[i] = item;
		return true;
	    }
	}
	return false;
    }

    public boolean add(T item) {
	if (alwaysStackable || item.getDefinitions().isStackable() || item.getDefinitions().isNoted()) {
	    for (int i = 0; i < data.length; i++) {
		if (data[i] != null) {
		    if (data[i].getId() == item
			    .getId() /*
				      * && data[i].getDegrade() ==
				      * /*item.getDegrade()
				      */) {
			data[i] = new Item(data[i].getId(), data[i].getAmount() + item.getAmount(),
				0/* item.getDegrade() */);
			return true;
		    }
		}
	    }
	} else {
	    if (item.getAmount() > 1) {
		if (freeSlots() >= item.getAmount()) {
		    for (int i = 0; i < item.getAmount(); i++) {
			int index = freeSlot();
			data[index] = new Item(item.getId(), 1,
				0/* item.getDegrade() */);
		    }
		    return true;
		} else {
		    return false;
		}
	    }
	}
	int index = freeSlot();
	if (index == -1) {
	    return false;
	}
	data[index] = item;
	return true;
    }

    public int freeSlots() {
	int j = 0;
	for (Item aData : data) {
	    if (aData == null) {
		j++;
	    }
	}
	return j;
    }

    public int remove(T item) {
	int removed = 0, toRemove = item.getAmount();
	for (int i = 0; i < data.length; i++) {
	    if (data[i] != null) {
		if (data[i].getId() == item.getId()) {
		    int amt = data[i].getAmount();
		    if (amt > toRemove) {
			removed += toRemove;
			amt -= toRemove;
			toRemove = 0;
			data[i] = new Item(data[i].getId(), amt);
			return removed;
		    } else {
			removed += amt;
			toRemove -= amt;
			data[i] = null;
		    }
		}
	    }
	}
	return removed;
    }

    public void removeAll(T item) {
	for (int i = 0; i < data.length; i++) {
	    if (data[i] != null) {
		if (data[i].getId() == item.getId()) {
		    data[i] = null;
		}
	    }
	}
    }

    public boolean containsOne(T item) {
	for (Item aData : data) {
	    if (aData != null) {
		if (aData.getId() == item.getId()) {
		    return true;
		}
	    }
	}
	return false;
    }

    public boolean contains(T item) {
	int amtOf = 0;
	for (Item aData : data) {
	    if (aData != null) {
		if (aData.getId() == item.getId()) {
		    amtOf += aData.getAmount();
		}
	    }
	}
	return amtOf >= item.getAmount();
    }

    public int freeSlot() {
	for (int i = 0; i < data.length; i++) {
	    if (data[i] == null) {
		return i;
	    }
	}
	return -1;
    }

    public void clear() {
	for (int i = 0; i < data.length; i++) {
	    data[i] = null;
	}
    }

    public int getSize() {
	return data.length;
    }

    public int getFreeSlots() {
	int s = 0;
	for (Item aData : data) {
	    if (aData == null) {
		s++;
	    }
	}
	return s;
    }
    
    public int getFreeSlots(Item... items) {
	int s = 0;
	for (Item aData : data) {
	    if (aData != null) {
		for (Item item : items) {
		    if (item.getId() == aData.getId())
			s++;
		}
	    } else if (aData == null)
		s++;
	}
	return s;
    }

    public int getUsedSlots() {
	int s = 0;
	for (Item aData : data) {
	    if (aData != null) {
		s++;
	    }
	}
	return s;
    }

    public int getNumberOf(Item item) {
	int count = 0;
	for (Item aData : data) {
	    if (aData != null) {
		if (aData.getId() == item.getId()) {
		    count += aData.getAmount();
		}
	    }
	}
	return count;
    }

    public int getNumberOf(int item) {
	int count = 0;
	for (Item aData : data) {
	    if (aData != null) {
		if (aData.getId() == item) {
		    count += aData.getAmount();
		}
	    }
	}
	return count;
    }

    public Item[] getItems() {
	return data;
    }

    public Item[] getShiftedItem() {
	int ptr = 0;
	for (Item item : data) {
	    if (item == null)
		continue;
	    ptr++;
	}
	Item[] items = new Item[ptr];
	ptr = 0;
	for (Item item : data) {
	    if (item == null)
		continue;
	    items[ptr++] = item;
	}
	return items;
    }

    public Item[] getItemsCopy() {
	Item[] newData = new Item[data.length];
	System.arraycopy(data, 0, newData, 0, newData.length);
	return newData;
    }

    public ItemsContainer<Item> asItemContainer() {
	ItemsContainer<Item> c = new ItemsContainer<Item>(data.length, this.alwaysStackable);
	System.arraycopy(data, 0, c.data, 0, data.length);
	return c;
    }

    public int getFreeSlot() {
	for (int i = 0; i < data.length; i++) {
	    if (data[i] == null) {
		return i;
	    }
	}
	return -1;
    }

    public int getThisItemSlot(T item) {
	return getThisItemSlot(item.getId());
    }

    public int getThisItemSlot(int itemId) {
	for (int i = 0; i < data.length; i++) {
	    if (data[i] != null) {
		if (data[i].getId() == itemId) {
		    return i;
		}
	    }
	}
	return -1;
    }

    public Item lookup(int id) {
	for (Item aData : data) {
	    if (aData == null) {
		continue;
	    }
	    if (aData.getId() == id) {
		return aData;
	    }
	}
	return null;
    }

    public int lookupSlot(int id) {
	for (int i = 0; i < data.length; i++) {
	    if (data[i] == null) {
		continue;
	    }
	    if (data[i].getId() == id) {
		return i;
	    }
	}
	return -1;
    }

    public void reset() {
	data = new Item[data.length];
    }

    public int remove(int preferredSlot, Item item) {
	int removed = 0, toRemove = item.getAmount();
	if (data[preferredSlot] != null) {
	    if (data[preferredSlot].getId() == item.getId()) {
		int amt = data[preferredSlot].getAmount();
		if (amt > toRemove) {
		    removed += toRemove;
		    amt -= toRemove;
		    toRemove = 0;
		    // data[preferredSlot] = new
		    // Item(data[preferredSlot].getDefinition().getId(), amt);
		    set2(preferredSlot, new Item(data[preferredSlot].getId(), amt));
		    return removed;
		} else {
		    removed += amt;
		    toRemove -= amt;
		    // data[preferredSlot] = null;
		    set(preferredSlot, null);
		}
	    }
	}
	for (int i = 0; i < data.length; i++) {
	    if (data[i] != null) {
		if (data[i].getId() == item.getId()) {
		    int amt = data[i].getAmount();
		    if (amt > toRemove) {
			removed += toRemove;
			amt -= toRemove;
			toRemove = 0;
			// data[i] = new Item(data[i].getDefinition().getId(),
			// amt);
			set2(i, new Item(data[i].getId(), amt));
			return removed;
		    } else {
			removed += amt;
			toRemove -= amt;
			// data[i] = null;
			set(i, null);
		    }
		}
	    }
	}
	return removed;
    }

    public void addAll(ItemsContainer<T> container) {
	for (int i = 0; i < container.getSize(); i++) {
	    T item = container.get(i);
	    if (item != null) {
		this.add(item);
	    }
	}
    }

    @SuppressWarnings("unchecked")
    public void addAll(Item[] container) {
	for (int i = 0; i < container.length; i++) {
	    Item item = container[i];
	    if (item != null) {
		this.add((T) item);
	    }
	}
    }

    public boolean hasSpaceFor(ItemsContainer<T> container) {
	for (int i = 0; i < container.getSize(); i++) {
	    T item = container.get(i);
	    if (item != null) {
		if (!this.hasSpaceForItem(item)) {
		    return false;
		}
	    }
	}
	return true;
    }

    private boolean hasSpaceForItem(T item) {
	if (alwaysStackable || item.getDefinitions().isStackable() || item.getDefinitions().isNoted()) {
	    for (Item aData : data) {
		if (aData != null) {
		    if (aData.getId() == item.getId()/*
						      * && aData.getDegrade() ==
						      * item.getDegrade()
						      */) {
			return true;
		    }
		}
	    }
	} else {
	    if (item.getAmount() > 1) {
		return freeSlots() >= item.getAmount();
	    }
	}
	int index = freeSlot();
	return index != -1;
    }

    public Item[] toArray() {
	return data;
    }
}
