/*
 * Created on 23.11.2004
 *
 */
package de.farafin.snEADy.world;



import de.farafin.snEADy.communication.D_GameInfo;
import de.farafin.snEADy.communication.D_Level;
import de.farafin.snEADy.communication.I_Constants;
import de.farafin.snEADy.communication.I_PlayFieldConstants;
import de.farafin.snEADy.communication.D_Vec2D;
import de.farafin.snEADy.GameParameter;
import java.util.Random;

//import GameParameter.D_ModuleCom.D_InitData;

/**
 * this class contains all calculations of the play field. If something moves or runns against a wall,
 * in world its the place where its calculated.
 * 
 * @author roland, lars
 * 
 * @version $Revision: 1.31 $
 */
public final class C_World implements I_World_interface, I_Constants, I_PlayFieldConstants
{
	//-------------------------------------------------------------------------------------
	//- attributes ------------------------------------------------------------------------
	//-------------------------------------------------------------------------------------

	// game variables
	/** arena of the game */
	private final C_Arena arena;
	
	/** next time a goody occures */
	private long nextGoodyTime = 0;
	
	/** computes the next time a goody occures */
	private Random randNextGoodyTime;
	
	/** computes the kind of goody than should placed */
	private Random randGoody;

	/** parameter set of GameParameter */
	private final GameParameter parameter;
	
//	/** optimizion for SD */
//	private D_Vec2D lastSDPos = null;
//	/** optimizion for SD */
//	private int lastSDQuadrant=0;
//	/** optimizion for SD */
//	private int lastSDCounter=0;
	/** only one segment per cycle */
	private long lastSDTime = 0;

//	/** a logfile to safe the players moves */
//	private C_LogFileWriter logFile;
	
	//-------------------------------------------------------------------------------------
	//- constructor -----------------------------------------------------------------------
	//-------------------------------------------------------------------------------------

	/** constructor
	 * @param gameInfo
	 */
	public C_World(D_GameInfo gameInfo, GameParameter parameter)
	{
		this.parameter = parameter;
		arena = new C_Arena(gameInfo, parameter);
		this.randNextGoodyTime = new Random();
		this.randGoody = new Random((long)(System.currentTimeMillis() * this.randNextGoodyTime.nextDouble()));
		
		this.nextGoodyTime = gameInfo.gameTime + this.randNextGoodyTime.nextInt(parameter.getMax_goody_occ_delay());
		

//		this.lastSDPos = new D_Vec2D(1, 1);
	}
	
	//-------------------------------------------------------------------------------------
	//- private methods -------------------------------------------------------------------
	//-------------------------------------------------------------------------------------
	
	/** sets a new goody and estimates the next goody time
	 * @param gameInfo
	 */
	private void placeGoody(D_GameInfo gameInfo)
	{
		D_Vec2D vec = new D_Vec2D();
		int trys = 0;
		int radnomNumber = 0;
		int range;
		int i = 0;
		C_Goody goody = null;
		
		// find a new field where to set the new goody
		do
		{
			vec.y = this.randGoody.nextInt(arena.getHeight());
			vec.x = this.randGoody.nextInt(arena.getWidth());
			trys++;
		}
		while(!arena.isFree(vec) && trys < 100);
		// estimate the kind of goody
		radnomNumber = this.randGoody.nextInt(
					  parameter.getGoody_speed_occ()
					+ parameter.getGoody_slowdown_occ()
					+ parameter.getGoody_length_occ()
					+ parameter.getGoody_points_occ()
					+ parameter.getGoody_shorter_occ()
					);

		// intervall test for the goody
		range = 0;
		// need this to have "break".. easier to implement that with a check of each intervall by hand..
		for(i=0; i<1 ;i++)
		{
			range += parameter.getGoody_speed_occ();
			if(radnomNumber < range)
			{
				goody = new C_GSpeed(vec, -1, this.parameter);
				break;
			}
			range += parameter.getGoody_slowdown_occ();
			if(radnomNumber < range)
			{
				goody = new C_GSpeed(vec, 1, this.parameter);
				break;
			}
			range += parameter.getGoody_length_occ();
			if(radnomNumber < range)
			{
				goody = new C_GLength(vec, parameter.getGoody_length_value(), this.parameter);
				break;
			}
			range += parameter.getGoody_points_occ();
			if(radnomNumber < range)
			{
				goody = new C_GPoints(vec, parameter.getGoody_points_value(), this.parameter);
				break;
			}
			range += parameter.getGoody_shorter_occ();
			if(radnomNumber < range)
			{
				goody = new C_GLength(vec, -parameter.getGoody_shorter_value(), this.parameter);
				break;
			}
		}
		
		// place the goody
		arena.objectAdd(goody);
		
		this.nextGoodyTime = gameInfo.gameTime + this.randNextGoodyTime.nextInt(parameter.getMax_goody_occ_delay());
	}
	

	//-------------------------------------------------------------------------------------
	//- public methods --------------------------------------------------------------------
	//-------------------------------------------------------------------------------------
	
	/* (non-Javadoc)
	 * @see de.farafin.GameParameter.world.I_World_interface#update(de.farafin.GameParameter.communication.D_GameInfo)
	 */
	public void update(D_GameInfo gameInfo)
	{
		int i;
		D_Vec2D vec = null;
//		int counter = 0, maxCounter =0;;
//		int height = 0;
//		int width = 0;
//		int quadrant = 0;
		int trys = 0;
		
		C_GameObject gObj;
		 
		for(i = 0; i < arena.getNumberOfObjects(); i++)
		{ 
			gObj = arena.getGameObject(i);
			if(gObj.getNextUpdateTime() >= 0 && gObj.getNextUpdateTime() <= gameInfo.gameTime)
			{
				arena.objectUpdate(gObj);
			}
		}

		if(parameter.getGameTime() < parameter.getSuddend_time())
		{
			if(nextGoodyTime <= gameInfo.gameTime)
			{
				placeGoody(gameInfo);
			}
		}
		// sudden Death
		//if(DEBUG) System.out.println("DEBUG C_World.update: suddenDeath " + gameInfo.suddenDeath);
		if(gameInfo.gameTime >= gameInfo.suddenDeath && gameInfo.gameTime > lastSDTime)
		{
			// print surface wall
			if(gameInfo.gameTime == gameInfo.suddenDeath)
			{
				
			}
			
			lastSDTime = gameInfo.gameTime;
/*			switch(parameter.SUDDEN_DEATH)
			{
			case 0:
				vec = new D_Vec2D();
				high = arena.getHigh();
				width = arena.getWidth();
				vec.copyOnMe(this.lastSDPos);
				counter = this.lastSDCounter;
				maxCounter = high * width;
				quadrant = this.lastSDQuadrant;
				// make a wall in spirals from the surface to the center
				while(counter <= maxCounter)
				{
					//System.out.println(vec.toString() + " at " + gameInfo.gameTime);
					// set wall on free field
					if(arena.isFree(vec))
					{
						arena.setCharOnPosition(vec, WALL);
						this.lastSDPos.copyOnMe(vec);
						this.lastSDQuadrant = quadrant;
						this.lastSDCounter = counter;
						break;
					}
					counter++;
					// next position
					switch(quadrant)
					{
						case 0:	// upper right line
							if(vec.x == width - (vec.y ))
							{
								vec.y++;
								quadrant = 1;
							}
							else vec.x++;
							break;
						case 1: // low right line
							if(high - vec.y == width - vec.x)
							{
								vec.x--;
								quadrant = 2;
							}
							else vec.y++;
							break;
						case 2: // low left line
							if(vec.y == high - vec.x)
							{
								vec.y--;
								quadrant = 3;
							}
							else vec.x--;
							break;
						case 3: // upper left line
							if(vec.y == vec.x + 1)
							{
								vec.x++;
								quadrant = 0;
							}
							else vec.y--;
							break;
						default:;
					}
				}
				break;
			case 1:*/

				// find a new field where to set the new goody
				vec = new D_Vec2D();
				trys = 0;
				do
				{
					vec.y = this.randGoody.nextInt(arena.getHeight());
					vec.x = this.randGoody.nextInt(arena.getWidth());
					trys++;
				}
				while(!arena.isFree(vec) && trys <= 100);
				if(trys < 100)
				{
					// place it on the field
					arena.objectAdd(new C_GLength(vec, -parameter.getGoody_shorter_value(), this.parameter));
				}
//				break;
//			default:;
//			}
		}
		
		updateInfos(gameInfo);
	}

	/* (non-Javadoc)
	 * @see de.farafin.GameParameter.world.I_World_interface#generateLevel()
	 */
	public D_Level generateLevel(){return this.arena.generateLevel();}

	/* (non-Javadoc)
	 * @see de.farafin.GameParameter.world.I_World_interface#updateLevel(de.farafin.GameParameter.communication.D_Level)
	 */
	public void updateInfos(D_GameInfo gameInfo)
	{
		arena.updateLevel(gameInfo.level);		
	}
	
	//-------------------------------------------------------------------------------------
	//- public static methods -------------------------------------------------------------
	//-------------------------------------------------------------------------------------
}