import java.util.ArrayList;

/**
 * The Rucksack class represents a rucksack that holds objects
 * of type PackingObject. Implement the predefined interface and
 * use it from RucksackProblem.java to solve a Rucksack problem
 * instance.
 */
public class Rucksack {

	/**
	 * Max capacity of the rucksack.
	 * 
	 * Is Initialized with 0
	 */
	private int m_capacity = 0;
	
	/**
	 * Current Value of all Objects in rucksack
	 * 
	 * Initialized with 0
	 */
	private int m_currentValue = 0;
	
	/**
	 * Current Weight of all Objects in rucksack.
	 * 
	 * Initialized with 0
	 */
	private int m_currentWeight = 0;
	
	/**
	 * ObjectList
	 * 
	 * Is initialized. (empty)
	 */
	//private LinkedList<PackingObject> m_objects = new LinkedList<PackingObject>();
	private ArrayList<PackingObject> m_objects = new ArrayList<PackingObject>();
	
	/**
	 * Do NOT MODIFY!!! -> For Copying and reading only
	 * @return
	 */
	public ArrayList<PackingObject> getObjects() {
		return m_objects;
	}

	/**
	 * Initializes a new Rucksack object with a given capacity. 
	 * Sets up all required data structures.
	 * 
	 * @param capacity The capacity of the Rucksack.
	 */
	public Rucksack(int capacity) {
		if(capacity >= 0)
		{
			this.m_capacity = capacity;
		} else
		{
			throw new IllegalArgumentException();
		}
	}
	
	/**
	 * Inserts an object into the Rucksack.
	 * 
	 * @param o The object to be added. 
	 * @throws ObjectAlreadyPresentException if the object is already present.
	 * @throws CapacityExceededException if the capacity is exceeded by the 
	 *         new object.
	 */
	void putObject(PackingObject o) {
		if(this.getTotalCapacity() >= this.getTotalWeight() + o.getWeight())
		{
			if(!this.contains(o))
			{
				this.m_objects.add(o);
				
				this.m_currentWeight += o.getWeight();
				this.m_currentValue  += o.getValue();
			} else
			{
				throw new ObjectAlreadyPresentException();
			}
		} else
		{
			throw new CapacityExceededException();
		}
	}
	
	/**
	 * Removes an object from the Rucksack.
	 * 
	 * @param o The object to be removed.
	 * @throws ObjectNotPresentException if the object is not in the rucksack.
	 */
	void removeObject(PackingObject o) {
		if(this.m_objects.remove(o))
		{
			this.m_currentValue  -= o.getValue();
			this.m_currentWeight -= o.getWeight();
		} else
		{
			throw new ObjectNotPresentException();
		}
	}
	
	/**
	 * Checks whether an object is contained in the Rucksack
	 * 
	 * @param o The object to check for.
	 * @return True if the object is present, false otherwise.
	 */
	public boolean contains(PackingObject o) {
		return this.m_objects.contains(o);
	}

	/**
	 * Returns the total weight of all objects in the rucksack.
	 */
	public int getTotalWeight() {
		/*int result = 0;
		
		for(int i = 0; i < this.m_objects.size(); i++)
		{
			result +=this.m_objects.get(i).getWeight();
		}
		
		return result;*/
		
		return this.m_currentWeight;
	}

	/**
	 * Returns the total value of all objects in the rucksack.
	 */
	public int getTotalValue() {

		/*int result = 0;
			
		for(int i = 0; i < this.m_objects.size(); i++)
		{
			result +=this.m_objects.get(i).getValue();
		}
		
		return result;*/
		
		return this.m_currentValue;
	}

	/**
	 * Returns the initial capacity of the rucksack.
	 */
	public int getTotalCapacity() {
		return this.m_capacity;
	}
	
	/**
	 * Returns the capacity of the Rucksack still available.
	 */
	public int getAvailableCapacity() {
		return this.getTotalCapacity() - this.getTotalWeight();
	}

	/**
	 * Copies the contents of another Rucksack to this Rucksack.
	 *  
	 * @param other The Rucksack to copy from.
	 */
	@SuppressWarnings("unchecked")
	public void copyFrom(Rucksack other) {
		if(other != null)
		{
			this.m_capacity = other.getTotalCapacity();
			this.m_objects =  (ArrayList<PackingObject>) other.getObjects().clone();
			this.m_currentWeight = other.getTotalWeight();
			this.m_currentValue = other.getTotalValue();
		}
	}

	/**
	 * Prints all objects in this Rucksack by iterating over a list of
	 * passed PackingObjects and checking for each object whether it
	 * is in the Rucksack using contains().
	 * 
	 * You can use this function for debugging purposes.
	 *  
	 * @param objects The complete list of objects that might be in the 
	 *                rucksack
	 * @return A string of the format [(value1,weight1), (value2,weight2), ...]
	 */
	public String toString(PackingObject[] objects) {
		StringBuilder sb = new StringBuilder();
		sb.append('[');
		for (PackingObject object : objects) {
			if (contains(object))
				sb.append(object.toString()).append(", ");
		}
		if (sb.length() > 1)
			sb.delete(sb.length() - 2, sb.length());
		sb.append(']');
		return sb.toString();
	}
}