/*
 * Created on 18.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 java.util.Iterator;
import java.util.Vector;

import de.farafin.snEADy.communication.D_GameInfo;
import de.farafin.snEADy.communication.D_Level;
import de.farafin.snEADy.communication.D_Vec2D;
import de.farafin.snEADy.communication.I_Constants;
import de.farafin.snEADy.communication.I_PlayFieldConstants;
import de.farafin.snEADy.GameParameter;
/**
 * @author roland
 * 
 * TODO what does that class do?
 * 
 * 
 * @version $Revision: 1.26 $
 */
public final class C_Arena implements I_Constants, I_PlayFieldConstants
{
	/** name of the arena */
	private final String name;
	
	/** height of the playfield */
	private final int height;
	
	/** width of the playfield */
	private final int width;

	/** the playfield of the game */
	private final char[][] playField;	

	/** the number of maximum player this level was designt for */
	private final int maxPlayer;

	/** parameter set of GameParameter */
	private final GameParameter parameter;

	/** Field gameObjects */
	private final Vector gameObjects;
	
	/** create - constructor
	 * 
	 * @param name
	 * @param height
	 * @param width
	 * @param playField
	 * @param maxPlayer
	 */
	protected C_Arena(String name, int height, int width, char[][] playField, int maxPlayer, GameParameter parameter)
	{
		super();
		int i=0;
		this.name = name;
		this.height = height;
		this.width = width;
		this.playField = new char[height][width];
		for(i = 0; i<height; i++)	for(int j = 0; j < width; j++)
				this.playField[i][j] = playField[i][j];
			
		this.maxPlayer = maxPlayer;
		this.gameObjects = new Vector();
		this.parameter = parameter;
	}

	/**  constructor
	 * @param name
	 * @param playField uses array-data to compute height and width
	 * @param maxPlayer max player of this arena
	 */
	protected C_Arena(String name, char[][] playField, int maxPlayer, GameParameter parameter)
	{
		super();
		int i=0;
		this.name = name;
		this.height = playField.length;
		this.width = playField[0].length;
		this.playField = new char[this.height][this.width];
		for(i = 0; i<height; i++)	for(int j = 0; j < width; j++)
				this.playField[i][j] = playField[i][j];
		
		this.maxPlayer = maxPlayer;
		this.gameObjects = new Vector();
		this.parameter = parameter;
	}
	
	/** constructor
	 * @param gameInfo creates a C_Arena out of D_GameInfo.
	 */
	protected C_Arena(D_GameInfo gameInfo, GameParameter parameter)
	{
		this(gameInfo.level.name, gameInfo.level.height, gameInfo.level.width, gameInfo.level.playField, gameInfo.level.maxPlayer, parameter);
		// TODO #roland: also generate gameObjects!

		int i = 0;
		int j = 0;
		C_GameObject gObj = null;

		for(i = 0; i < this.height; i++)
		{
			for(j = 0; j < width; j++)
			{
				// if the position is a snake-char
				if(this.isSnake(i, j))
				{
					this.setFree(i, j);
				}
				
				if(this.isGoody(i, j))
				{
					gObj = null;
					switch(this.playField[i][j])
					{
						case SPEED: gObj = new C_GSpeed(new D_Vec2D(i,j), -1 , parameter); break;
						case LENGTH: gObj = new C_GLength(new D_Vec2D(i,j), parameter.getGoody_length_value() , parameter); break;
						case POINTS:  gObj = new C_GPoints(new D_Vec2D(i,j), parameter.getGoody_points_value() , parameter); break;
						case SLOW: gObj = new C_GSpeed(new D_Vec2D(i,j), 1 , parameter); break;
						case SHORT: gObj = new C_GLength(new D_Vec2D(i,j), -parameter.getGoody_shorter_value() , parameter); break;
						default: gObj = null;
					}
					
					if(gObj != null)
					{
						this.objectAdd(gObj);
					}
				}
			}
		}
		
		for(i = 0; i < gameInfo.playerData.length; i++)
		{
			if(gameInfo.playerData[i].snakeStatus == IN_ACTION) this.objectAdd(new C_Snake(gameInfo.playerData[i], parameter));
		}
	}
	
	/** if at position y,x is placed a C_GameObject, it returns the Object.
	 * O(Obj_count) 
	 * 
	 * @param y y - coordinate
	 * @param x x - coordinate
	 * @return the Oby on y, x, if there is no Obj, then return is null
	 */
	protected C_GameObject getGOofPos(int y, int x)
	{
		return this.getGOofPos(new D_Vec2D(y, x));
	}

	/** if at position of vec is placed a C_GameObject, it returns the Object.
	 * O(Obj_count*sizeOfObject) 
	 * 
	 * @param vec the vector of the fied that should be controlled
	 * @return the Obj on vec, if there is no Obj, then return is null
	 */
	protected C_GameObject getGOofPos(D_Vec2D vec)
	{
		C_GameObject gObj = null;
		D_Vec2D curPos = null;
		char c = this.getCharOf(vec);
		Iterator iter1 = null;
		int i = 0;
		
		if(isFree(c) || isWall(c)) return null;
		
		// itteration over all gameObjects
		for(iter1 = this.gameObjects.iterator(); iter1.hasNext();)
		{
			gObj = (C_GameObject) iter1.next();			
			if(c != gObj.ownChar) continue;
			
			// itteraton over all positions this object is placed
			for(i = 0; i<gObj.objPositions.size(); i++)
			{
				curPos = (D_Vec2D) gObj.objPositions.getElementAt(i); 
				if(curPos.equals(vec)) return gObj;
			}
		}
		
		if(DEBUG) System.out.println("DEBUG C_Arena: WARNING!! field in Arena is marked but no GameObject fits!");
		return null;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		String result = null;
		int i;
		
		result =  "Name: " + name + "\n";
		result += "Size: " + width + "x" + height + "\n";
		result += "MaxPlayer: " + maxPlayer + "\n";
		result += "PlayField:\n";
		for(i=0; i<height; i++) result += String.valueOf(playField[i]) + "\n";
		result += "GameObjects: " + "\n";
		for(i=0; i<this.gameObjects.size(); i++) result += "\t" + i + ": " + ((C_GameObject)this.gameObjects.get(i)).toString() + "\n";
		
		return result;
	}
	

	/**
	 * @param field The playField to set.
	 * @return true if set was successful.. false if the size doesnt fit.
	 */
	protected boolean setPlayField(char[][] field)
	{
		int i, j;

		if(field == null) return false;
		if(field.length == 0) return false;
		if(field.length != height || field[0].length != width)
			return false;
		
		for(i = 0; i < playField.length; i++)
			for(j = 0; j < playField[0].length; j++)
				this.playField[i][j] = field[i][j];
		return true;
	}

	/**
	 *  sets at the coordiantes <code>y</code> and <code>x</code> the character <code>c</code> 
	 * @param y line
	 * @param x row
	 * @param c character to set
	 * @return true if coordinate exists, false otherwise
	 */
	protected boolean setCharOnPosition(int y, int x, char c)
	{
		if(y < 0 || height < y || x < 0 || width < x) return false;
		playField[y][x] = c;
		return true;
	}
	 
	/** sets at the coordiantes of <code>vec</code> the character <code>c</code>
	 * @param vec coordinate
	 * @param c character to set
	 * @return true if coordinate exists, false otherwise
	 * 
	 * @see #setCharOnPosition(int, int, char)
	 */
	protected boolean setCharOnPosition(D_Vec2D vec, char c)
	{ return setCharOnPosition(vec.y, vec.x, c);}
	

	/**
	 *  sets at the coordiantes <code>y</code> and <code>x</code> a free field 
	 * @param y line
	 * @param x row
	 * @return true if coordinate exists, false otherwise
	 * 
	 * @see #setCharOnPosition(int, int, char)
	 */
	protected boolean setFree(int y, int x)
	{return setCharOnPosition(y, x, '.');}

	/** sets at the coordiantes of <code>vec</code> a free field
	 * @param vec coordinate
	 * @return true if coordinate exists, false otherwise
	 * 
	 * @see #setCharOnPosition(int, int, char)
	 */
	protected boolean setFree(D_Vec2D vec)
	{ return setCharOnPosition(vec.y, vec.x, '.');}
	
	/** 
	 * @param gameObj
	 */
	protected void objectAdd(C_GameObject gameObj)
	{
		this.gameObjects.add(gameObj);
		D_Vec2D [] posArray = gameObj.getAllPosArray();
		for(int i = 0; i < posArray.length; i++)
		{
			this.setCharOnPosition(posArray[i], gameObj.getOwnChar());
		}
	}

	/** 
	 * @param gameObj the object that should be removed from the list
	 * @return if remove was sucessfull (if not, the reference was still out of the list)
	 */
	protected boolean objectDel(C_GameObject gameObj)
	{
		return this.gameObjects.remove(gameObj);
	}

	/**
	 * @param index the index of the GameObject that should be rmoved
	 * @return the removed GameObject
	 */
	protected C_GameObject objectDel(int index)
	{
		return (C_GameObject)this.gameObjects.remove(index);
	}

	/**  update the status of the given gameObject. if the game Object likes to run,
	 * use this method to tell him to
	 * 
	 *  @param gameObj the object that should be updated
	 *  
	 * @see C_GameObject
	 */
	protected void objectUpdate(C_GameObject gameObj)
	{
		gameObj.update(this);
	}
	
	/**
	 * @param number specifies the C_GameObject
	 * @return the specified GameObject
	 * 
	 * @see C_GameObject
	 */
	protected C_GameObject getGameObject(int number){return (C_GameObject)this.gameObjects.get(number);}
	
	/**
	 * @return number of stored GameObjects
	 * 
	 * @see C_GameObject
	 */
	protected int getNumberOfObjects(){return this.gameObjects.size();}
	
	/** tests if the coordinates are element of the playField
	 * @param y line
	 * @param x row
	 * @return true if the coordiantes are in height - and width - renge
	 */
	protected boolean isFieldOfArena(int y, int x)
	{
		if(y < 0 || height <= y || x < 0 || width <= x) return false;
		return true;
	}
	/** tests if the coordinates are element of the playField
	 * @param vec coordinate as 2D-vector
	 * @return true if the coordiantes are in height - and width - renge
	 */
	protected boolean isFieldOfArena(D_Vec2D vec){return isFieldOfArena(vec.y, vec.x);}
	
	/**
	 * @return returns a copy of the playField.
	 */
	protected char[][] getPlayFieldCopy()
	{
		char[][] pField = new char[playField.length][];
		for(int i = 0; i < pField.length; i++)
		{
			pField[i] = new char[playField[i].length];
		}
		for(int i = 0; i < pField.length; i++)
		{
			for(int j = 0; j < pField[i].length; j++)
			{
				pField[i][j] = playField[i][j];
			}
		}
		
		return pField;
	}
	
	/** returns the character of the specified coordinates
	 * @param y line number
	 * @param x row number
	 * @return the charactor of the specified field and '?' if the field does not exist
	 */
	protected char getCharOf(int y, int x)
	{
		if(!isFieldOfArena(y, x)) return '?';
		return playField[y][x];
	}

	/** returns the character of the specified coordinates
	 * @param vec the vector of the field that should returned
	 * @return the charactor of the specified field
	 */
	protected char getCharOf(D_Vec2D vec){return getCharOf(vec.y, vec.x);}
	
	/** 
	 * @param l lineNumber
	 * @return the line, null if line doesnt exist
	 */
	protected char[] getLineCopy(int l)
	{
		char[] line = new char[height];
		if(l < 0 || l > height-1) return null;
		for(int i = 0; i < height; i++)
		{
			line[i] = this.playField[l][i];
		}
		return line;
	}
	
	/** height
	 * @param r rowNumber
	 * @return the row, null if row doesnt exist
	 */
	protected char[] getRowCopy(int r)
	{
		char[] row = new char[width];
		if(r < 0 || r > width-1) return null;
		for(int i = 0; i < width; i++)
		{
			row[i] = this.playField[i][r];
		}
		return row;
	}
	
	/** @return Returns the height. */
	protected int getHeight(){return height;}
	/** @return Returns the maxPlayer. */
	protected int getMaxPlayer(){return maxPlayer;}
	/** @return Returns the name. */
	protected String getName(){return name;}
	/** @return Returns the width. */
	protected int getWidth(){return width;	}

	/**
	 * @param c char to test
	 * @return true if the char is free
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isFree(char c){return c == FREE;}

	/**
	 * @param c char to test
	 * @return true if the char is a Wall
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isWall(char c){return c == WALL;}

	/**
	 * @param c char to test
	 * @return true if the char is a Snake
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isSnake(char c){return (c >= PLAYER_0 && c <= PLAYER_9);}

	/**
	 * @param c char to test
	 * @return true if the char is a Goody
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isGoody(char c){return c >= LENGTH && c <= GOODY_z;}

	/**
	 * @param c char to test
	 * @return true if the char is a Special Field
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isSpecialField(char c){return c >= EXIT && c <= FIELD_Z;}
	
	/**
	 * @param y line
	 * @param x row
	 * @return true if there is a WALL at the coordinates
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isFree(int y, int x)
	{
		if(!isFieldOfArena(y, x)) return false;
		return playField[y][x] == FREE;
	}

	/**
	 * @param y line
	 * @param x row
	 * @return true if there is a WALL at the coordinates
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isWall(int y, int x)
	{
		if(!isFieldOfArena(y, x)) return false;
		return playField[y][x] == WALL;
	}

	/**
	 * @param y line
	 * @param x row
	 * @return true if there is a goody at the coordinates
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isSnake(int y, int x)
	{
		if(!isFieldOfArena(y, x)) return false;
		return (playField[y][x] >= PLAYER_0 && playField[y][x] <= PLAYER_9);
	}
		
	/**
	 * @param y line
	 * @param x row
	 * @return true if there is a goody at the coordinates
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isGoody(int y, int x)
	{
		if(!isFieldOfArena(y, x)) return false;
		return (playField[y][x] >= LENGTH && playField[y][x] <= GOODY_z);
	}
	
	/**
	 * @param y line
	 * @param x row
	 * @return true if there is a goody at the coordinates
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isSpecialField(int y, int x)
	{
		if(!isFieldOfArena(y, x)) return false;
		return (playField[y][x] >= EXIT && playField[y][x] <= FIELD_Z);
	}

	/**
	 * @param vec controll this position
	 * @return true if there is a goody at <code>vec</code> position
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isFree(D_Vec2D vec){	return isFree(vec.y, vec.x);}

	/** 
	 * @param vec controll this position
	 * @return true if there is a goody at <code>vec</code> position
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isWall(D_Vec2D vec){return isWall(vec.y, vec.x);}

	/**
	 * @param vec controll this position
	 * @return true if there is a goody at <code>vec</code> position
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isSnake(D_Vec2D vec){return isSnake(vec.y, vec.x);}
		
	/**
	 * @param vec controll this position
	 * @return true if there is a goody at <code>vec</code> position
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isGoody(D_Vec2D vec){return isGoody(vec.y, vec.x);}

	/**
	 * @param vec controll this position
	 * @return true if there is a goody at <code>vec</code> position
	 * 
	 * @see I_PlayFieldConstants
	 */
	protected boolean isSpecialField(D_Vec2D vec){	return isSpecialField(vec.y, vec.x);}
		
	
	/**
	 * @return a copy of the ocal level
	 */
	protected D_Level generateLevel()
	{		
		return new D_Level(this.name, this.height, this.width, this.getPlayFieldCopy(), this.maxPlayer);
	}

	/**
	 * @param level the lvel the local level should be stored in
	 * @return if the coppy was succasfull. it fales if the size of level and the local level doesnt fit.
	 */
	protected boolean updateLevel(D_Level level)
	{
		if(level.height != this.height || level.width != this.width) return false;
		
		for(int i = 0; i < level.height; i++)
		{
			for(int j = 0; j < level.width; j++)
			{
				level.playField[i][j] = this.playField[i][j];
			}
		}
		
		return true;
	}
	
	/** generates the funktion:<br>
	 * k: (i)<br>
	 * 1: (0, 1, 0, -1)<br>
	 * 2: (0, 1, 2, 1, 0, -1, -2, -1)<br>
	 * 3: (0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1)<br>
	 * ...<br>
	 * @param i index in (...)
	 * @param k index of line
	 * @return value at position
	 */
	private int fy(int i, int k)
	{
		if(i <= k) return i;
		if(i <= k+k+k) return ((k+k) - i);
		return  (i - (k<<2));
	}

	/** generates the funktion:<br>
	 * k: (j)<br>
	 * 1: (-1, 0, 1, 0)<br>
	 * 2: (-2, -1, 0, 1, 2, 1, 0, -1)<br>
	 * 3: (-3, -2, -1, 0, 1, 2, 3, 2, 1, 0, -1, -2)<br>
	 * ...<br>
	 * @param j index in (...)
	 * @param k index of line
	 * @return value at position
	 */
	private int fx(int j, int k)
	{
		return ( (j<=k+k)? j - k : k+k+k-j );
	}

	/** returns all surrounding fields of vec in an array that is ordered that way:<br>
	 * ___C___<br>
	 * __N4D__<br>
	 * _MB05E_<br>
	 * LA3#16F<br>
	 * _K927G_<br>
	 * __J8H__<br>
	 * ___I___<br>
	 * <br>
	 * the vectors in that positions are absolute positions in the arena.
	 * but they are valculated relative to the origin '#'<br>
	 * the absolut number of elements are: maxDist * (maxDist + maxDist + 2)<br>
	 * 4, 12, 40, 60, 84, 112, ... 
	 * @param vec the origin
	 * @param maxDist how many layer should be calculated
	 * @return returns the array of positions
	 */
	protected D_Vec2D[] getSourounding(D_Vec2D vec, int maxDist)
	{
		// 4, 12, 24, 40, 60, 84, ....
		D_Vec2D[] fields = new D_Vec2D[ maxDist * (maxDist + maxDist + 2) ];

		int k=0, k2=0, i=0, index=0;
		
		index = 0;
		// k: ring counter
		for(k=1; k<=maxDist; k++)
		{
			k2 = k<<2;
			// i: counter of the element in the ring, coordinate from 0,0 as origin are calculated with fy, fx
			for(i=0; i<k2; i++)
			{
				fields[index] = new D_Vec2D((fy(i, k) + vec.y + this.height)%this.height, (fx(i, k) + vec.x + this.width)%this.width);
				index++;
			}
		}
		
		return fields;
	}
}
