/**
 * --Copyright notice-- 
 *
 * Copyright (c) School of Geography, University of Leeds. 
 * http://www.geog.leeds.ac.uk/
 * This software is licensed under 'The Artistic License' which can be found at 
 * the Open Source Initiative website at... 
 * http://www.opensource.org/licenses/artistic-license.php
 * Please note that the optional Clause 8 does not apply to this code.
 *
 * The Standard Version source code, and associated documentation can be found at... 
 * [online] http://mass.leeds.ac.uk/
 * 
 *
 * --End of Copyright notice-- 
 *
 */

import java.util.*;


/**
* Nibbler agent demonstrates how to implement behaviour, check and alter an environment, and communicate with other agents using method calls.
* The nibbler will move around randomly, but checks that its percieved neighbourhood doesn't contain any other agents by running through all 
* the agents asking where they are and checking if they fall in its percieved area. This is the root of a partial knowledge system (we only 
* interact with those we have 'knowledge' of because they fall in our perceived area). Once the nibbler has found a safe spot, it checks 
* whether there is any value in that location in the Environment, and nibbles it down if there is.<P>
* Note that the spatial x, y, and neighbourhood details are inherited from 'Animal'.<P>
* To do: Crowded nibblers with nowhere to move to will currently loop forever. Need a stopping condition adding. See "run()".
* @version 1.0
* @author <A href="http://www.geog.leeds.ac.uk/people/a.evans/">Andy Evans</A>
*/
public class Nibbler extends Animal {


	/** For eating environment data. */
	private double eatingRate = 10.0;
	
	/** For eating environment data. */
	private double fullUp = -1.0;
	
	/** For eating environment data. */
	private double stomach = 0.0;

	
	/**
	* Constructor. Sets a random x and y based on current world size, but 
	* otherwise defers to Animal.
	* @param worldIn environment for the model
	* @param agentsIn list of all the agents in the model
	* @param eatingRateIn rate of eating environment data
	* @param stomach maximum amount of environment data carriable
	*/
	public Nibbler (Environment worldIn, ArrayList agentsIn, double eatingRateIn, double fullUpIn) {

		super(worldIn, agentsIn);
		x = (int)(Math.random() * world.getWidth());
		y = (int)(Math.random() * world.getHeight());
		eatingRate = eatingRateIn;
		fullUp = fullUpIn;

	} 
	
	
	
	
	/**
	* Moves the nibbler from its current location to a random position one step away.
	* The new location must be suitable. Then interacts with the environment.
	*/
	public void run () {
	
	
		if (world.getDataValue(x, y) == 0) {
			move();
		}	 

		interactWithEnvironment();
		
	}
	
	
	
	
	/**
	* Moves the nibbler from its current location to a random position one step away.
	* The new location must be in the range of the associated environment.
	*/
	private void move() {
	
		do {
		
			int x = 0;
			do {
				x = this.x;
				double randomNumber = Math.random();
				if (randomNumber < 0.33) x--;
				if (randomNumber > 0.66) x++;
			} while ((x < 0) || (x > world.getWidth() - 1));
			this.x = x;
				
			int y = 0;
			do {
				y = this.y;
				double randomNumber = Math.random();
				if (randomNumber < 0.33) y--;
				if (randomNumber > 0.66) y++;
			} while ((y < 0) || (y > world.getHeight() - 1));
			this.y = y;
		
		} while (locationSuitable(x,y) == false);
		
	}
	
	
	
	
	/**
	* Checks a prospective location for suitability.
	* In this case checks there are no agents within the potential percieved neighbourhood around the 
	* new location.
	* @param x spatial x coordinate
	* @param y spatial y coordinate
	* @return whether the neighbourhood is suitable (i.e. doesn't contain another agent in its neighbourhood)
	*/
	private boolean locationSuitable(int x, int y) {
	
		for (Agent agent: agents) {
		
			if ((getNeighbourhood(x,y).contains(agent.getX(), agent.getY()) == true) && (agent != this)) {
			
				return false;
			
			}
		}
		return true;
	
	}
	
	

	
	/**
	* Checks the value in the environment at the location, and 'nibbles' it down a bit 
	* if there is values to nibble.
	*/
	private void interactWithEnvironment () {

		if (world.getDataValue(x, y) > eatingRate) {
			world.setDataValue(x, y, world.getDataValue(x, y) - eatingRate);
			stomach = stomach + eatingRate;
		} else {
			stomach = stomach + world.getDataValue(x, y);
			world.setDataValue(x, y, 0);
		}
		if (fullUp != -1) checkFull(); // Wouldn't run unless fullUp set.

	}

	
	
	
	/**
	* Checks if the Nibbler is full and resets stomach/location if they are.
	*/
	private void checkFull() {

		if (stomach >= fullUp) {
			stomach = 0;
			x = (world.getWidth() - 1) / 2;
			y = world.getHeight() - 1;
		}
	} 

	
}