/*
 * Created on 05.12.2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package de.farafin.snEADy.world;

import de.farafin.snEADy.communication.D_PlayerData;
import de.farafin.snEADy.communication.D_Vec2D;
import de.farafin.snEADy.communication.I_Constants;
import de.farafin.snEADy.communication.RingVector;
import java.util.Random;

import javax.swing.plaf.SplitPaneUI;

import de.farafin.snEADy.GameParameter;

/** this is the snake.. the body of the player controlled objects
 * @author roland
 * 
 * @version $Revision: 1.51 $
 */
public final class C_Snake extends C_GameObject implements I_Constants
{
	/** status of the snake
	 *  
	 * @see de.farafin.snEADy.communication.I_Constants#IN_ACTION
	 * @see de.farafin.snEADy.communication.I_Constants#IN_HEAVEN
	 * @see de.farafin.snEADy.communication.I_Constants#IN_EXIT
	 * @see de.farafin.snEADy.communication.I_Constants#IN_ERROR_TIME
	 * @see de.farafin.snEADy.communication.I_Constants#IN_ERROR_SPACE
	 * @see de.farafin.snEADy.communication.I_Constants#IN_ERROR_INIT
	 * @see de.farafin.snEADy.communication.I_Constants#IN_ERROR_EXC
	 */
	protected int status = IN_ACTION;
	
	/** the length of the snake */
	protected int length = 1;
	
	/** Every LENGTH_RATE's killed segment, the snake gets one segment longer, so here is the status stored.. */
	protected float lengthRateStatus = 0.0f;

	/**reallize the autoGrow*/
	protected long nextAutoGrow;
	
	/** for realizing autoslow.. */
	protected long nextAutoSlow;
	
	/** the "pilot" of the snake. the controlling element. */
	protected D_PlayerData pilot;
	
	/** for checking if something changed */
	private int pilotLength = 0;
	/** for checking if something changed */
	private long pilotDelay = 0;
	
	/** the survival bonus a snake gets if it reaches the exit */
	protected int survivalBonus = 0;
	
	/** the visable length of the snake.. at the beginning or if the snake changes its length,
	 * the visable length might be bigger or smaller as the length is actually. than the snake 
	 * grows or reduces its length only one element per cycle */
	//private int visLength = 1;
	
	/** default constructor */
	protected C_Snake()
	{
		super();
		this.status = IN_ACTION;
		this.length = 1;
		this.pilot = null;
		this.nextAutoGrow = parameter.getAuto_grow_delay();
		this.nextAutoSlow = parameter.getAuto_slowdown_delay();
		this.survivalBonus = 0;
	}
	
	/** create Constructor
	 * @param headPosition
	 * @param faceDirection
	 * @param objPositions
	 * @param ownChar
	 * @param status
	 * @param length
	 * @param pilot
	 * @param waitCycles
	 * @param parameter
	 */
	protected C_Snake(D_Vec2D headPosition, int faceDirection, RingVector objPositions, char ownChar, int status, int length, D_PlayerData pilot, long waitCycles, GameParameter parameter)
	{
		super(headPosition, faceDirection, objPositions, ownChar, waitCycles, parameter);
		this.status = status;
		this.length = length;
		this.pilot = pilot;	// reference copy
		this.nextAutoGrow = parameter.getAuto_grow_delay();
		this.nextAutoSlow = parameter.getAuto_slowdown_delay();
		this.survivalBonus = 0;
	}

	/** constructor
	 * @param headPosition
	 * @param faceDirection
	 * @param ownChar
	 * @param length
	 * @param pilot
	 * @param waitCycles
	 * @param parameter
	 */
	protected C_Snake(D_Vec2D headPosition, int faceDirection, char ownChar, int length, D_PlayerData pilot, long waitCycles, GameParameter parameter)
	{
		super(headPosition, faceDirection, ownChar, waitCycles, parameter);
		this.status = IN_ACTION;;
		this.length = length;
		//for(int i=0; i<length-1; i++)	{this.objPositions.addLast(headPosition.clone());}
		this.pilot = pilot;	// reference copy
		this.nextAutoGrow = parameter.getAuto_grow_delay();
		this.nextAutoSlow = parameter.getAuto_slowdown_delay();
		this.survivalBonus = 0;
	}
	/** constructor
	 * @param headPosition
	 * @param faceDirection
	 * @param ownChar
	 * @param pilot
	 * @param waitCycles
	 * @param parameter
	 */
	protected C_Snake(D_Vec2D headPosition, int faceDirection, char ownChar, D_PlayerData pilot, long waitCycles, GameParameter parameter)
	{
		super(headPosition, faceDirection, ownChar, waitCycles, parameter);
		this.status = IN_ACTION;
		this.length = 1;
		this.pilot = pilot;
		this.nextAutoGrow = parameter.getAuto_grow_delay();
		this.nextAutoSlow = parameter.getAuto_slowdown_delay();
		this.survivalBonus = 0;
	}

	/** smart Constructor
	 * @param headPosition
	 * @param faceDirection
	 * @param objPositions
	 * @param ownChar
	 * @param length
	 * @param pilot
	 * @param waitCycles
	 * @param parameter
	 */ 
	protected C_Snake(D_Vec2D headPosition, int faceDirection, RingVector objPositions, char ownChar, int length, D_PlayerData pilot, long waitCycles, GameParameter parameter)
	{
		super(headPosition, faceDirection, objPositions, ownChar, waitCycles, parameter);
		this.status = IN_ACTION;
		this.length = length;
		this.pilot = pilot;
		this.nextAutoGrow = parameter.getAuto_grow_delay();
		this.nextAutoSlow = parameter.getAuto_slowdown_delay();
		this.survivalBonus = 0;
	}

	/**
	 * @param playerData
	 * @param parameter
	 */
	protected C_Snake(D_PlayerData playerData, GameParameter parameter)
	{
		super(playerData.headPos, playerData.turnDirection, playerData.ownChar, playerData.waitCycles, parameter);
		this.status = IN_ACTION;
		this.length = parameter.getInit_length();
		this.pilot = playerData;
		//this.visLength = 1;
		this.nextAutoGrow = parameter.getAuto_grow_delay();
		this.nextAutoSlow = parameter.getAuto_slowdown_delay();
		this.survivalBonus = 0;
	}
	
	/** computes the kill points for other snakes if this one looses a segment or is killed
	 * @param arena
	 * @param snake
	 * @param died
	 */
	private void computeKillPoints(C_Arena arena, C_Snake snake, boolean died)
	{
		char c = FREE;
		int snakeCounter = 0;
		boolean snakeUnknown = true;
		C_Snake []snakes = new C_Snake[MAX_PLAYERS];
		int []snakesDist = new int[MAX_PLAYERS];
		int i, j, k;
		//D_Vec2D tmpVec = new D_Vec2D();
		//int addPoints = 1;
		int distConst = 0;
		D_Vec2D vecArray[];
		
		if(died) distConst = parameter.getKill_points_radius();
		else distConst = parameter.getDamage_points_radius();
		
		vecArray = arena.getSourounding(this.headPosition, distConst);
		for(i=0; i<vecArray.length; i++)
		{
			c = arena.getCharOf(vecArray[i]);
			if(arena.isSnake(c) && c != this.ownChar)
			{
				snakeUnknown = true;
				for(k = 0; k < snakeCounter; k++)
				{
					if(snakes[k].ownChar == c)
					{
						snakeUnknown = false;
						//if(k==0) System.out.println("dist = " + this.headPosition.dist(tmpVec) + " min = " + snakesDist[k]);
						if(this.headPosition.dist(vecArray[i]) < snakesDist[k])
						{
							snakesDist[k] = this.headPosition.dist(vecArray[i]);
						}
					}
				}
				if(snakeUnknown)
				{
					snakes[snakeCounter] = (C_Snake)arena.getGOofPos(vecArray[i]);
					snakesDist[snakeCounter] = this.headPosition.dist(vecArray[i]);
					snakeCounter++;
				}
			}
		}
		
		// add points
		for(k = 0; k < snakeCounter; k++)
		{
			snakes[k].pilot.killPoints += (1 << (distConst - snakesDist[k]));
		}

		if(snake == null) return;
		
		// give extra to the snake this. run into. but only if its not it self
		if(this.ownChar != snake.ownChar)
		{
			//if(died) snake.lengthRateStatus += LENGTH_SNAKE_KILL_RATE;
			//else snake.lengthRateStatus += LENGTH_SEGMENT_KILL_RATE;

			if(!died) snake.lengthRateStatus += parameter.getDamage_length_grow();
			
			//if(DEBUG) System.out.println("DEBUG C_Snake.computeKillPoints: ");
			snake.pilot.killPoints += 1 << (distConst-1);
		}
		// this is extra for the contest to make it easier to gain points!!
		else
		{
			if(parameter.getEasy_points() > 0)
			{
				snakeCounter = 0;
				snakes = new C_Snake[MAX_PLAYERS];
				
				//find all Snakes that that are touching this snake
				// trav over all own segments
				for(j=0; j<this.objPositions.size(); j++)
				{
					// find sourrounding points
					vecArray = arena.getSourounding(((D_Vec2D)this.objPositions.getElementAt(j)), 1);
					// trav over all sourrounding points
					for(i=0; i<vecArray.length; i++)
					{
						// get char on field
						c = arena.getCharOf(vecArray[i]);

						// if char is snake but not own snake
						if(arena.isSnake(c) && c != this.ownChar)
						{
							
							// make sure only unknown snaked are added
							snakeUnknown = true;
							for(k = 0; k < snakeCounter; k++)
							{
								if(snakes[k].ownChar == c)
								{
									snakeUnknown = false;
								}
							}
							if(snakeUnknown)
							{
								snakes[snakeCounter] = (C_Snake)arena.getGOofPos(vecArray[i]);
								snakeCounter++;
							}
						}
					}
				}
				
				// give snakes points
				for(i=0; i<snakeCounter; i++)
				{
					snakes[i].pilot.killPoints += parameter.getEasy_points();
				}
			}
		}
	}
	
	
	/**
	 * @param arena
	 * @param n the number of segmnets that should removed
	 * @return the last deleted vector
	 */
	private D_Vec2D removeLastSegments(C_Arena arena, int n)
	{
		int i = 0;
		D_Vec2D vec = null;
		for(i = 0; i<n; i++)
		{
			if(this.objPositions.size() <= 1)
			{
				return vec;
			}
			vec = (D_Vec2D) this.objPositions.remLast();
			arena.setFree(vec);
			this.length--;
		}
		
		 return vec;
	}
	
	/**
	 *  @param arena
	 * 	@param lastPos
	 *  */
	private void actualizateSnakeLength(C_Arena arena, D_Vec2D lastPos)
	{
		if(this.length > this.objPositions.size())
		{
			this.objPositions.addLast(lastPos.clone());
			arena.setCharOnPosition(lastPos, this.ownChar);
		}
		else if(this.length < this.objPositions.size())
		{
			arena.setFree((D_Vec2D)this.objPositions.remLast());
		}
	}
	
	/** kills the snake
	 * @param arena the arena the snake is living in
	 * @param killer if the snake was killed by an other snake, this is it. otherwise killer is null
	 */
	private void die(C_Arena arena, C_Snake killer)
	{
		int i=0, trys=0;
		D_Vec2D vec = new D_Vec2D();
		this.computeKillPoints(arena, killer, true);
		Random randGoody;
	
		System.out.println("DIE");
		
		while(this.objPositions.size() > 0)
		{
			arena.setFree((D_Vec2D)this.objPositions.remFirst());
		}
		
		this.objPositions.clear();
		this.length = 0;
		
		if(this.pilot.snakeStatus == IN_ACTION)
		{
			this.status = IN_HEAVEN;
			this.pilot.snakeStatus = IN_HEAVEN;
		}
			
		randGoody = new Random();
		// spread out some goodies
		for(i=0; i<parameter.getKill_point_goodies(); i++)
		{
			trys = 0;
			do
			{
				// place the goody in a radius of POINTS_SNAKE_KILL_RADIUS
				vec.y = (this.headPosition.y + randGoody.nextInt(2*parameter.getKill_points_radius() + 1) - parameter.getKill_points_radius() + arena.getHeight())%arena.getHeight();
				vec.x = (this.headPosition.x + randGoody.nextInt(2*parameter.getKill_points_radius() + 1) - parameter.getKill_points_radius() + arena.getWidth())%arena.getWidth();
				trys++;
			}
			while(!arena.isFree(vec) && trys <= 50);
			if(trys < 50)
			{
				arena.objectAdd(new C_GPoints(vec, parameter.getGoody_points_value(), this.parameter));
			}
		}
	}
	
	/** let the object move one field to the direction of its head it is necessary,
	 * that there it is computed here what happands if the object runns into a wall..
	 * @param arena
	 */
	protected void moveToFace(C_Arena arena)
	{
		D_Vec2D lastPos = null;
		D_Vec2D nextPos = this.getNextPosInFaceDirection(arena);
		C_Snake snake = null;
		C_GameObject obj = null;
		char nextChar = arena.getCharOf(nextPos);
		int i;
		
		if(this.status == IN_EXIT)
		{

			lastPos = removeLastSegments(arena, 1);
			this.turnHeadBack();
			

			// die if too short
			if(this.objPositions.size() <= 1)
			{	
				while(this.objPositions.size() > 0)
				{
					arena.setFree((D_Vec2D)this.objPositions.remFirst());
				}
				
				this.objPositions.clear();
				this.length = 0;
				
			}
			return;
		}
				
		// what will happend to the snake depends on the field its going to 
		if(arena.isFree(nextChar)) // just move forward
		{
			// get last pos and moves the reference to the beginning of the snake
			lastPos = (D_Vec2D)((D_Vec2D)this.objPositions.moveLastToFirst()).clone();

			// actualizes head position
			this.headPosition.copyOnMe(nextPos);
			// actuallizes the first position of the body.. it is the snakes last position before because of the reference copy above.
			((D_Vec2D)this.objPositions.getFirst()).copyOnMe(nextPos);

			// cleans the last field from arena
			arena.setFree(lastPos);
			// set the snake-char to the new head position
			arena.setCharOnPosition(nextPos, this.ownChar);	
		}
		else if(arena.isGoody(nextChar))
		{
			C_Goody goody = ((C_Goody)arena.getGOofPos(nextPos));
			
			// get last pos and moves the reference to the beginning of the snake
			lastPos = (D_Vec2D)((D_Vec2D)this.objPositions.moveLastToFirst()).clone();

			// actualizes head position
			this.headPosition.copyOnMe(nextPos);
			// actuallizes the first position of the body.. it is the snakes last position before because of the reference copy above.
			((D_Vec2D)this.objPositions.getFirst()).copyOnMe(nextPos);

			// cleans the last field from arena
			arena.setFree(lastPos);
			// set the snake-char to the new head position
			arena.setCharOnPosition(nextPos, this.ownChar);	
			
			goody.wasEaten(this, arena);
		}
		else if(arena.isSpecialField(nextChar))
		{
			switch(nextChar)
			{
				case EXIT:
				{
					if(parameter.getExit_time() <= parameter.getGameTime())	// exit is open
					{
						this.status = IN_EXIT;
						this.nextAutoSlow = parameter.getGameTime() + 10 * this.length;
						this.nextAutoGrow = parameter.getGameTime() + 10 * this.length;
						this.length = 0;
						this.waitCycles = 1;
						this.nextUpdateTime = parameter.getGameTime() + 1;
						
						// give all snakes points!!
						for(i=0; i<arena.getNumberOfObjects();i++)
						{	
							obj = arena.getGameObject(i);
							if(arena.isSnake(obj.ownChar))
							{
								((C_Snake) obj).survivalBonus += parameter.getSurvival_points();
							}
						}
						this.pilot.killPoints += this.survivalBonus;
						
						lastPos = removeLastSegments(arena, 1);
						this.turnHeadBack();
						
						return;
					}
					// else  exit is closed (no else-tag because if block has no exit: return ..)
					{
						lastPos = removeLastSegments(arena, 1);
						this.turnHeadBack();
						
						this.computeKillPoints(arena, null, false);
						/*
						// die if too short
						if(this.objPositions.size() <= 1)
						{	
							// there are extra points if the snake runns into an other snake.. thats here computed.
							if(arena.isSnake(nextChar))
							{
								snake = (C_Snake)arena.getGOofPos(nextPos);
								if(snake.ownChar == this.ownChar) snake = null;
							}
							else snake = null;

							this.die(arena, snake);
						}*/
					}
					break;
				}
				default: // treat the spetial field like as if it were empty...
				{
					// get last pos and moves the reference to the beginning of the snake
					lastPos = (D_Vec2D)((D_Vec2D)this.objPositions.moveLastToFirst()).clone();

					// actualizes head position
					this.headPosition.copyOnMe(nextPos);
					// actuallizes the first position of the body.. it is the snakes last position before because of the reference copy above.
					((D_Vec2D)this.objPositions.getFirst()).copyOnMe(nextPos);

					// cleans the last field from arena
					arena.setFree(lastPos);
					// set the snake-char to the new head position
					arena.setCharOnPosition(nextPos, this.ownChar);
				}
			}
			
		}
		else if(arena.isSnake(nextChar))
		{
			// the snake that this snake run into
			snake = (C_Snake)arena.getGOofPos(nextPos);
			
			// removes SEGMENT_LOSE_RATE from the snake and updates the length as well.
			lastPos = removeLastSegments(arena, 2);
			
			// turns the head into its original position, so it is the same as if it didnt move.
			this.turnHeadBack();
			// give other snakes its points
			this.computeKillPoints(arena, snake, false);
			/*
			// die if too short
			if(this.objPositions.size() <= 1)
			{	
				// there are extra points if the snake runns into an other snake.. thats here computed.
				if(arena.isSnake(nextChar))
				{
					snake = (C_Snake)arena.getGOofPos(nextPos);
					if(snake.ownChar == this.ownChar) snake = null;
				}
				else snake = null;

				this.die(arena, snake);
			}*/
		}
		else if(arena.isWall(nextChar))
		{
			lastPos = removeLastSegments(arena, 1);
			this.turnHeadBack();
			
			this.computeKillPoints(arena, null, false);
			/*
			// die if too short
			if(this.objPositions.size() <= 1)
			{	
				// there are extra points if the snake runns into an other snake.. thats here computed.
				if(arena.isSnake(nextChar))
				{
					snake = (C_Snake)arena.getGOofPos(nextPos);
					if(snake.ownChar == this.ownChar) snake = null;
				}
				else snake = null;
				
				this.die(arena, snake);
			}*/
		}
		else
		{
			if(DEBUG) System.out.println("DEBUG C_Snake: WARNING: Undifined field in Playfield" + nextPos + ": '" + arena.getCharOf(nextPos)+ "'");
			return;
		}
		

		//if(this.ownChar == PLAYER_0) if(DEBUG) System.out.println("DEBUG C_Snake.moveToFace: " + lastPos.toString() + " length " + this.length + " fields " + this.objPositions.size());
		actualizateSnakeLength(arena, lastPos);
		
		// die if too short
		if(this.objPositions.size() <= 1)
		{	
			// there are extra points if the snake runns into an other snake.. thats here computed.
			if(arena.isSnake(nextChar))
			{
				snake = (C_Snake)arena.getGOofPos(nextPos);
				if(snake.ownChar == this.ownChar) snake = null;
			}
			else snake = null;
			
			this.die(arena, snake);
		}
	}

	/** if the length state is bigger than one, the snake gets longer..
	 *  */
	private void updateLengthState()
	{
		// if the status is bigger than 1, it should be added...
		if(this.lengthRateStatus >= 1)
		{
			this.length += (int)this.lengthRateStatus;
			this.lengthRateStatus -= ((int)this.lengthRateStatus);
		}	
		if(parameter.getGameTime() >= this.nextAutoGrow)
		{
			this.nextAutoGrow = parameter.getGameTime() + parameter.getAuto_grow_delay();
			this.length++;
		}
	}
	
	/** if speed State is more than one, the snake gets slower
	 */
	private void updateSpeedState()
	{
		// if the status is bigger than 1, it should be added...
		if(parameter.getGameTime() >= this.nextAutoSlow)
		{
			this.nextAutoSlow = parameter.getGameTime() + parameter.getAuto_slowdown_delay();
			this.waitCycles++;
		}
		if(parameter.getMax_move_delay() <= this.waitCycles)
		{
			this.waitCycles = parameter.getMax_move_delay();
		}
		
		this.pilot.waitCycles = this.waitCycles;
	}
	
	
	/** updates the own informations with the snake infos */
	protected void updatePilot()
	{
		this.pilot.nextUpdateTime = this.nextUpdateTime + this.waitCycles;
		this.pilot.waitCycles = this.waitCycles;
		this.pilot.length = this.objPositions.size();
		this.pilot.headPos.copyOnMe(this.headPosition);
		this.pilot.watchDirection = this.faceDirection;
		this.pilot.snakeStatus = this.status;
		
		this.pilot.turnDirection = TURN_NONE;
	}
	
	/** turns the head to the direction pilot*/
	private void turnHead(C_Arena arena)
	{
		int tmp = 0;
		if(isBlocked(getCharDirHead(arena, this.pilot.turnDirection)))
		{
			this.pilot.turnDirection = TURN_NONE;
		}
		
		tmp = this.faceDirection + this.pilot.turnDirection;
		
		if(tmp < FACE_NORTH) this.faceDirection = FACE_WEST;
		else if(tmp > FACE_WEST) this.faceDirection = FACE_NORTH;
		else this.faceDirection = tmp;
	}

	/** turns the head to the opposide direction of what pilot sais pilot*/
	private void turnHeadBack()
	{
		int tmp = this.faceDirection - this.pilot.turnDirection;
		
		if(tmp < FACE_NORTH) this.faceDirection = FACE_WEST;
		else if(tmp > FACE_WEST) this.faceDirection = FACE_NORTH;
		else this.faceDirection = tmp;
	}
	
	/* (non-Javadoc)
	 * @see de.farafin.GameParameter.world.C_GameObject#update(de.farafin.GameParameter.world.C_Arena)
	 */
	protected void update(C_Arena arena)
	{
		if(this.pilot.snakeStatus == IN_EXIT && this.objPositions.size() > 0) this.pilot.move = true;

		// if the player produced an error
		if(this.pilot.snakeStatus > 2)
		{
			if(this.status <= 2) // if the snake is still alive, but the pilot is 'dead'
			{
				this.status = this.pilot.snakeStatus;
				this.die(arena, null); // kill snake too
			}
			return;
		}
		
		// if the snake is not allowed to move now, its returned
		if(!this.pilot.move)
		{
			this.pilot.snakeUpdated = false;
			return;
		}

		
		// if the snake was alowed to move, it is not allowed to move an other time.
		this.pilot.move = false;
		this.pilot.snakeUpdated = true;
		
		this.pilotLength = this.pilot.length;
		this.pilotDelay = this.pilot.waitCycles;
				
		switch(this.status)
		{
		case IN_ACTION:
			// best position for the update...
			updateLengthState();
			updateSpeedState();
			// turns the head of the snake in the given direction
			this.turnHead(arena);
			// moves the snake
			this.moveToFace(arena);
			// update informations for the pilot
			this.updatePilot();
			break;
		case IN_HEAVEN:
			this.updatePilot();
			break;
		case IN_EXIT:
			this.moveToFace(arena);		
			this.updatePilot();		
			break;
		default:;
		}
		
		if(this.waitCycles > 0) this.nextUpdateTime += this.waitCycles;
		
		// pilot length
		if(this.pilotLength < this.pilot.length) this.pilot.lengthChanged = 1;
		else if(this.pilotLength > this.pilot.length) this.pilot.lengthChanged = -1;
		else this.pilot.lengthChanged = 0;

		// pilot delay
		if(this.pilotDelay < this.pilot.waitCycles) this.pilot.delayChanged = 1;
		else if(this.pilotDelay > this.pilot.waitCycles) this.pilot.delayChanged = -1;
		else this.pilot.delayChanged = 0;
		
		this.pilot.snakeUpdated = true;
	}
	

	/** checs which char is in direction of <code>direction<\code>.
	 * @param arena
	 * @param direction
	 * @return the field at arena
	 */
	private char getCharDirHead(C_Arena arena, int direction)
	{
		int dir = this.faceDirection + direction;
		int height = arena.getHeight();
		int width = arena.getWidth();
		int y=0, x=0;

		if(dir < FACE_NORTH) dir = FACE_WEST;
		else if(dir > FACE_WEST) dir = FACE_NORTH;

		switch(dir)
		{
			case FACE_NORTH:
				y = (this.headPosition.y-1+height)%height;
				x = this.headPosition.x;
				break;
			case FACE_EAST:
				y = this.headPosition.y;
				x = (this.headPosition.x+1)%width;
				break;
			case FACE_SOUTH:
				y = (this.headPosition.y+1)%height;
				x = this.headPosition.x;
				break;
			case FACE_WEST:
				y = this.headPosition.y;
				x = (this.headPosition.x-1+width)%width;
				break;
		}

		return arena.getCharOf(y, x);
	}

	/**
	 * @param c
	 * @return if its locked or not
	 */
	private boolean isBlocked(char c)
	{
		if(c == WALL) return true;
		if(PLAYER_0 <= c && c <= PLAYER_9)  return true;
		if(c == EXIT && parameter.getGameTime() < parameter.getExit_time())  return true;
		return false;
	}
	
	/** turns the head of the object to <code>left_right</code> 
	 * @param arena
	 * @param left_right turn direction
	 *
	 * @see I_Constants
	 */
	/*
	protected void turnTo(C_Arena arena, int left_right)
	{
		switch(left_right)
		{
		case TURN_NONE:
			break;
		case TURN_LEFT:
			if(isBlocked(getCharDirHead(arena, TURN_LEFT)))
			{
				if(DEBUG) System.out.println("DEBUG C_Snake.turnTo: DO NOT TURN LEFT");
				this.pilot.turnDirection = TURN_NONE;
			}
			else
			{
				if(DEBUG) System.out.println("DEBUG C_Snake.turnTo: char = " + getCharDirHead(arena, TURN_LEFT));
				this.faceDirection = (this.faceDirection <= 0)? 3 : this.faceDirection - 1;
			}
			break;
		case TURN_RIGHT:

			if(isBlocked(getCharDirHead(arena, TURN_RIGHT)))
			{
				if(DEBUG) System.out.println("DEBUG C_Snake.turnTo: DO NOT TURN RIGHT");
				this.pilot.turnDirection = TURN_NONE;
			}
			else
			{
				if(DEBUG) System.out.println("DEBUG C_Snake.turnTo: char = " + getCharDirHead(arena, TURN_RIGHT));
				this.faceDirection = (this.faceDirection >= 3)? 0 : this.faceDirection + 1;
			}
			break;
		default:
			break;
		}
	}*/

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.farafin.GameParameter.C_GameObject#jumpTo(de.farafin.GameParameter.D_Vec2D)
	 */
	protected void jumpTo(D_Vec2D pos, C_Arena arena)
	{
	// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see de.farafin.GameParameter.C_GameObject#getAllPosSorted(int)
	 */
	protected D_Vec2D[] getAllPosSorted(int sort)
	{
		// TODO #roland: sorting is needed!
		return (D_Vec2D[])this.objPositions.toArray();
	}
	
	
	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	protected Object clone()
	{
		C_Snake snake = new C_Snake();
		snake.faceDirection = this.faceDirection;
		snake.headPosition = (D_Vec2D)this.headPosition.clone();
		snake.length = this.length;
		snake.nextUpdateTime = this.nextUpdateTime;
		snake.objPositions = (RingVector)this.objPositions.clone();
		snake.ownChar = this.ownChar;
		snake.pilot = this.pilot;
		snake.status = this.status;
		snake.waitCycles = this.waitCycles;
		
		return snake;
	}
	
	
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		String result = "Snake:\n";
		
		result += "\tHead: " + this.headPosition.toString() + "\n";
		result += "\tFace: " + ((this.faceDirection <= FACE_EAST)? ((this.faceDirection < FACE_EAST)? "UP" : "RIGHT") : ((this.faceDirection < FACE_WEST)? "DOWN" : "LEFT" )) + "\n";
		result += "\tLength: " + this.length + "\n";
	//	result += "\tvisLength: " + this.visLength + "\n";
		result += "\tChar: " + this.ownChar + "\n";
		result += "\tStatus: " + this.status + "\n";
		result += "\tPositions: \t";
		for(int i=0; i< this.objPositions.size(); i++)
		{
			result += " " + ((D_Vec2D)this.objPositions.getElementAt(i)).toString();
		}
		
		
		return result;
	}
	
	/**
	 * @return Returns the pilot.
	 */
	protected D_PlayerData getPilot()
	{
		return pilot;
	}
}
