package knowledgeAndTechnics;
import gameObjects.AbsPosSettable;
import gameObjects.Ball;
import gameObjects.Flag;
import gameObjects.GameObject;
import gameObjects.Line;
import gameObjects.ObjectInvisibleException;
import gameObjects.Player;

import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import messages.PassMessage;
import utilities.Geometrie;
import utilities.NullVektorException;
import utilities.Vektor;

//TODO: Sicherstellen, dass die Werte konsistent sind

//import com.sun.rsasign.p;

/*
 * Created on Sep 14, 2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */

/******************************************************************************
 * @author Soccer Gruppe 1
 * Beinhaltet Informationen ueber die Umgebung und stellt 
 * Standard-Methoden (Techniken) zur Spielersteuerung bereit
 ******************************************************************************/
public class Intelligence {

	private BaseFunctions baseFunctions;

	private final String teamName;
	
	//Serverparameter:
	private float goalWidth = 14.02f; //TODO: goalWidth erkennen
	public static int INERTIA_MOMENT = 5;
	
	public final static int PLAY_DIRECTION_LEFT = -1;
	public final static int PLAY_DIRECTION_RIGHT =  1;
	private final static int MESSAGE_CAPACITY = 10;
	private int playDirection = 0;
	
	/**Die uniform number des Spielers*/
	private int unum = 111;
	
	public final static int BALL_POSSESSION_ME = 1;
	public final static int BALL_POSSESSION_WE = 2;
	public final static int BALL_POSSESSION_OPPONENT = 3;
	private int ballPossession = BALL_POSSESSION_OPPONENT;
	private int personToCover = -1;
	private int lastKickTime = Integer.MIN_VALUE;
	
	/*************************************************
	 * Timeout fuer dribbeln
	 *************************************************
	 */
	private final static int DRIBBLE_TIMEOUT = 8;
	
	/*************************************************
	 * Timeout fuer passen
	 *************************************************
	 */
	private final static int PASS_TIMEOUT = 20;
	
	/*************************************************
	 * Timeout fuer Pass annehmen
	 *************************************************
	 */
	private final static int GET_PASS_TIMEOUT = 20;
	
	/*************************************************
	 * Timeout fuer "wenn er jetzt nicht getroffen hat,
	 * dann ist der Ball futsch"
	 *************************************************
	 */
	private static final int DIRECT_TIMEOUT = 0;
	
	//TODO: ballPossession muß noch implementiert werden.
//***********************************************
//* Die Ausgangsposition des Spielers
//*	 Handlungsbereich
//************************************************
	private double xPlayerStart;
	private double yPlayerStart;
	
	private double xPlayerHome;
	private double yPlayerHome;
	private double x1PlayerActingField;
	private double y1PlayerActingField;
	private double x2PlayerActingField;
	private double y2PlayerActingField;
	
	private double x1PlayerFreeKickField;
	private double y1PlayerFreeKickField;
	private double x2PlayerFreeKickField;
	private double y2PlayerFreeKickField;
	
	
	private boolean playerAlive=true;
	private boolean checkOuterState=true;
	
	
	private int role;
	
	public static final int GOALIE = 1;
	public static final int DEFENSE_LEFT = 2;
	public static final int DEFENSE_CENTER = 3;
	public static final int DEFENSE_RIGHT = 4;
	
	
//*******************************************************
//* unsere Position
//***********************************************************************
	private double xPlayer;
	private double yPlayer;
	private double viewDirection;
	private double bodyDirection;
	private double speedXrelBody;
	private double speedYrelBody;
	private double speedXabs;
	private double speedYabs;
	
	private double speed;
	

//******************************************
//* Zeiten der Nachrichten
//******************************************	
	private int seeTime = -3; //Willkuerlicher Satrtwert, damit ungleiche Werte zu Beginn
	private int senseBodyTime = -7;
	private int hearTime;

//********************************
//* Sense Body Variablen		 *
//********************************
//TODO: Bodysense in Zahlen ?bergeben
	private String viewWidth, viewQuality;
	private double stamina, effort;
	private double amountOfSpeed, directionOfSpeed;
	private double headDirection;
	private int kickCount, dashCount, turnCount, sayCount, turnNeckCount, catchCount, moveCount, changeViewCount, armCount, focusCount, tackleCount;
	private String armMovable, armExpires;
	private double armTarget_Zahl1, armTarget_Zahl2;
	private String focusTarget;
	private String tackleExpires;
	
//*******************************
//* PlayerParam variablen
//*******************************
	//TODO: kickableMarginDeltaMax ueberpruefen
	private float kickableMarginDeltaMin = 0, 
	kickableMarginDeltaMax = 0.7f; 
	
	private float catchableAreaL;
	private float catchableAreaW;
//*******************************
//*	Hear Variablen
//*******************************
	 
	//PlayModes
	static public final int BEFORE_KICK_OFF = 0;
	static public final int PLAY_ON= 1;
	static public final int TIME_OVER= 2;
	static public final int KICK_OFF_WE= 3;
	static public final int KICK_OFF_OPPONENT= 4;

    /**************************************************************************
     * Die maximale Geschwindigkeit, bei der sich de Wert fuer Stamina nicht 
     * verringert.
     **************************************************************************/
    public static final int STAMINA_NEUTRAL_SPEED = 93;
    
	static public final int KICK_IN_WE = 5;
	static public final int KICK_IN_OPPONENT = 6;
	static public final int FREE_KICK_WE = 7;
	static public final int FREE_KICK_OPPONENT = 8;	
	static public final int CORNER_KICK_WE = 9;
	static public final int CORNER_KICK_OPPONENT = 10;
	static public final int GOAL_KICK_WE = 11;
	static public final int GOAL_KICK_OPPONENT = 12;
	static public final int GOAL_WE = 13;
	static public final int GOAL_OPPONENT = 14;
	static public final int DROP_BALL = 15;
	static public final int OFFSIDE_WE = 16;
	static public final int OFFSIDE_OPPONENT = 17;
	static public final int GOAL_WE_N = 18;
	static public final int GOAL_OPPONENT_N = 19;
	static public final int FOUL_WE = 20;
	static public final int FOUL_OPPONENT = 21;
	static public final int GOALIE_CATCH_BALL_WE = 22;
	static public final int GOALIE_CATCH_BALL_OPPONENT = 23;
	static public final int TIME_UP_WITHOUT_A_TEAM = 24;
	static public final int TIME_UP = 25;
	static public final int HALF_TIME = 26;
	static public final int TIME_EXTENDED = 27;
	
	
	
	private int playMode = BEFORE_KICK_OFF;
	private int expectedPlayMode = BEFORE_KICK_OFF;
	//FIFO-Queue fuer Player-Nachrichten:
	private PassMessage passMessage = null;
	private int lastContact = -1;
	
//*******************************
//* See Variablen
//*******************************	
	
	public Hashtable flags = new Hashtable();
	private Hashtable lines = new Hashtable();
	public Hashtable seenFlags = new Hashtable();
	private Vector seenLines = new Vector();
	private Vector players = new Vector();
	private Ball ball = new Ball(0, 0, 0);

    private Ball oldBall = new Ball(0, 0, 0);
	
    private Ball lastSpeedBall;
	
//************************************
//Constructors
//************************************
	public Intelligence(String teamName){
		this.teamName = teamName;
	}
	
	
//**************************************************
//*  Methoden zum zur?cksetzen des InformationPools
//*  wenn eine entsprechende Nachricht eingetroffen ist
//**************************************************

	public void newSee() {
		Iterator i = players.iterator();
		//TODO: Spielervektor ueberdenken;
		//Nur genau gesehene Spieler werden gemerkt
		Vector newPlayers = new Vector();
		while(i.hasNext()) {
			Player p = (Player) i.next(); 
			if (p.isSeen()) newPlayers.add(p);
		}
		players = newPlayers;
		
		//ball = null;
		seenFlags.clear();
		seenLines.clear();		
	}
	
	public void newHear(){
	}
	
	//newSenseBody war unnoetig, weil Nachricht immer vollstaendig
	

//****************************************
//*  Bereich fuer Flags								
//****************************************

  private void insertFlag(String name, float absX, float absY){
	  flags.put(name, new Flag(absX, absY));
  }

  public void insertFlags(){
	  insertFlag("c", 0 * playDirection, 0);
		
	  insertFlag("l t", -52.5f * playDirection, -34 * playDirection);
	  insertFlag("c t", 0 * playDirection,      -34 * playDirection);
	  insertFlag("r t", +52.5f * playDirection, -34 * playDirection);
		
	  insertFlag("l b", -52.5f * playDirection, +34 * playDirection);
	  insertFlag("c b",      0 * playDirection, +34 * playDirection);
	  insertFlag("r b", +52.5f * playDirection, +34 * playDirection);
		
	  insertFlag("t l 50", -50 * playDirection, -39 * playDirection);
	  insertFlag("t l 40", -40 * playDirection, -39 * playDirection);
	  insertFlag("t l 30", -30 * playDirection, -39 * playDirection);
	  insertFlag("t l 20", -20 * playDirection, -39 * playDirection);
	  insertFlag("t l 10", -10 * playDirection, -39 * playDirection);
	  insertFlag("t 0",      0 * playDirection, -39 * playDirection);
	  insertFlag("t r 50",  50 * playDirection, -39 * playDirection);
	  insertFlag("t r 40",  40 * playDirection, -39 * playDirection);
	  insertFlag("t r 30",  30 * playDirection, -39 * playDirection);
	  insertFlag("t r 20",  20 * playDirection, -39 * playDirection);
	  insertFlag("t r 10",  10 * playDirection, -39 * playDirection);
	  //untere Reihe:
	  insertFlag("b l 50", -50 * playDirection, 39 * playDirection);
	  insertFlag("b l 40", -40 * playDirection, 39 * playDirection);
	  insertFlag("b l 30", -30 * playDirection, 39 * playDirection);
	  insertFlag("b l 20", -20 * playDirection, 39 * playDirection);
	  insertFlag("b l 10", -10 * playDirection, 39 * playDirection);
	  insertFlag("b 0",      0 * playDirection, 39 * playDirection);
	  insertFlag("b r 50",  50 * playDirection, 39 * playDirection);
	  insertFlag("b r 40",  40 * playDirection, 39 * playDirection);
	  insertFlag("b r 30",  30 * playDirection, 39 * playDirection);
	  insertFlag("b r 20",  20 * playDirection, 39 * playDirection);
	  insertFlag("b r 10",  10 * playDirection, 39 * playDirection);
	  //linke Reihe:
	  insertFlag("l t 30", -57.5f * playDirection, -30 * playDirection);
	  insertFlag("l t 20", -57.5f * playDirection, -20 * playDirection);
	  insertFlag("l t 10", -57.5f * playDirection, -10 * playDirection);
	  insertFlag("l 0",    -57.5f * playDirection,   0 * playDirection);				
	  insertFlag("l b 30", -57.5f * playDirection,  30 * playDirection);
	  insertFlag("l b 20", -57.5f * playDirection,  20 * playDirection);
	  insertFlag("l b 10", -57.5f * playDirection,  10 * playDirection);				
		
	  insertFlag("r t 30", 57.5f * playDirection, -30 * playDirection);
	  insertFlag("r t 20", 57.5f * playDirection, -20 * playDirection);
	  insertFlag("r t 10", 57.5f * playDirection, -10 * playDirection);
	  insertFlag("r 0",    57.5f * playDirection,   0 * playDirection);				
	  insertFlag("r b 30", 57.5f * playDirection,  30 * playDirection);
	  insertFlag("r b 20", 57.5f * playDirection,  20 * playDirection);
	  insertFlag("r b 10", 57.5f * playDirection,  10 * playDirection);				
		
	  //Strafraum:
	  insertFlag("p l t", -36 * playDirection, -20 * playDirection);
	  insertFlag("p l c", -36 * playDirection,  0  * playDirection);
	  insertFlag("p l b", -36 * playDirection, 20  * playDirection);		

	  insertFlag("p r t", 36 * playDirection, -20 * playDirection);
	  insertFlag("p r c", 36 * playDirection,   0 * playDirection);
	  insertFlag("p r b", 36 * playDirection,  20 * playDirection);
		
	  //linkes Tor:
	  insertFlag("g l t", -52.5f * playDirection, -goalWidth / 2 * playDirection);
	  insertFlag("g l",   -52.5f * playDirection, 0 * playDirection);
	  insertFlag("g l b", -52.5f * playDirection, goalWidth / 2 * playDirection);
	  //rechtes Tor:
	  insertFlag("g r t", 52.5f * playDirection, -goalWidth / 2 * playDirection);
	  insertFlag("g r",   52.5f * playDirection, 0 * playDirection);
	  insertFlag("g r b", 52.5f * playDirection, goalWidth / 2 * playDirection);
  }
	
	
	public Flag getFlag(String name){
		return (Flag) flags.get(name);
	}

// Gesehene Fahnen in einen Vektor einfuegen
	public void insertSeenFlag(String name, float dist, float dir, float distChange, float dirChange, int time) {
		Flag f =getFlag(name); 
		f.setData(dist, dir, distChange, dirChange, time);
		seenFlags.put(name, f);
	}
	
	public void insertSeenFlag(String name, float dist, float dir, int time) {
		Flag f = getFlag(name);
		f.setData(dist, dir, time);
		seenFlags.put(name, f);
	}

	public Flag getSeenFlag(String name){
		return (Flag) seenFlags.get(name);
	}

//	****************************************
//	*Bereich fuer Lines 									*
//	****************************************
	private void insertLine(String name, float absDir){
		lines.put(name, new Line(absDir));
	}
	public void insertLines(){
		insertLine("t",0);
		insertLine("b",0);
		insertLine("r",90);
		insertLine("l",90);	
	}
	
	public Line getLine(String name){
		return (Line) lines.get(name);
	}

	public void insertSeenLine(String name, float dist, float dir) {
		Line l = getLine(name);
		l.setDist(dist);
		l.setDir(dir);
		seenLines.add(l);
	}
	  

//	*****************************************
//	* Bereich fuer den Ball                              *
//	*****************************************
	
	/**
	 * Gibt den Ball zur&uuml;ck, der in der letzten Runde gesehen wurde
	 * @return Der Ball, falls gesehen, ansonsten null.
	 */
	public Ball getBall() {
		return ball;
	}

    /**
     * @return Returns the lastSpeedBall.
     */
    public Ball getLastSpeedBall() {
        return lastSpeedBall;
    }
    /**
     * @return Returns the oldBall.
     */
    public Ball getOldBall() {
        return oldBall;
    }
	public void insertSeenBall(float dist, float dir, float distChange, float dirChange, int time){
		oldBall = ball;
		ball = new Ball(dist, dir, distChange, dirChange, time);
	}
	
	public void insertSeenBall(float dist, float dir, int time){
	    oldBall = ball;
	    ball = new Ball(dist, dir, time);
	}
	
	
//*****************************************
//* Bereich fuer die Player                              *
//*****************************************
	

//	Informationen aus 4 unterschiedlichgen Distanzen UNUM, TEAM, INVISIBLE, TOUCHED
	
	public void insertTouchedPlayer(float dist, float dir, int time){
			players.addElement( new Player(dist, dir, time, Player.TOUCHED_PLAYER));
		}
	
	public void insertInvisiblePlayer(float dist, float dir, int time){
		players.addElement( new Player(dist, dir, time, Player.INVISIBLE_PLAYER));
	}
	
	public void insertTeamPlayer(int team, float dist, float dir, int time){
		players.addElement(new Player(team,dist,dir, time, Player.TEAM_PLAYER));
	}
	
	public void insertUnumPlayer(int team, int unum, float dist, float dir, float distChange, float dirChange, float bodyDir, float headDir, int time){
		Iterator i = players.iterator();
		while (i.hasNext()){
			Player p = (Player) i.next();
			try{
				if((p.getMode() == 3) && (p.getUnum() == unum)) {
					i.remove();
				}
			}catch (ObjectInvisibleException e){
				System.err.println("insertUnumPlayer: Code sollte nicht ausgefuehrt werden.");
			}
		}
		players.addElement(new Player(team,unum,dist,dir,distChange,dirChange,bodyDir,headDir, time, Player.UNUM_PLAYER ));
	}


	public Vector getSeenPlayers(){
		return players;
	}
	
//*****************************************************************************
//			 Position Berechnen		************************************************
//*****************************************************************************	
	
	public synchronized void calculatePositions(){				
		Flag f1= null, f2 = null;
		Iterator e = seenFlags.values().iterator();
		//Ermittlung von zwei sichtbaren Flaggen
		while((f1 == null) && e.hasNext()){
			f1 = (Flag) e.next();
			//TODO: Nullpunkt und Strafraumflaggen in Berechnung einbeziehen
			//if (((f1.getAbsX() == 0) && (f1.getAbsY() == 0)) ||(Math.abs(f1.getAbsX()) == 36)) f1 = null;
		} 
		while((f2 == null) && e.hasNext()){
			f2 = (Flag) e.next();
			//if (((f2.getAbsX() == 0) && (f2.getAbsY() == 0)) || ((f2.getAbsX() == 0) && (f2.getAbsY() == 0))) f2 = null;			 
		}

		//TODO: Sinnvolle Fehlerbehandlung bei unbekannter Position
		if (f1 == null) {System.err.println("1.Fahne nicht gesehen");return;};
		if (f2 == null) {System.err.println("2.Fahne nicht gesehen");return;}; 
		

		//Damit immer derselbe Fall bei der Berechnung genommen werden kann:
		if (f1.getDir() > f2.getDir()){
			Flag fTausch = f1;
			f1 = f2;
			f2 = fTausch;	
		}		
		/*Vektor pos;
		try{
		pos =  Geometrie.getPosition(new Vektor (f1.getAbsX(), f1.getAbsY()), new Vektor(f2.getAbsX(), f2.getAbsY()), f1.getDist(), f2.getDist(), f1.getDir(), f2.getDir());
		
		}catch(ArithmeticException ae){
		    pos =  Geometrie.getPosition2(new Vektor (f1.getAbsX(), f1.getAbsY()), new Vektor(f2.getAbsX(), f2.getAbsY()), f1.getDist(), f2.getDist(), f1.getDir(), f2.getDir());
		    System.err.println("alt:flaw");
		}
		Vektor pos2 =  Geometrie.getPosition2(new Vektor (f1.getAbsX(), f1.getAbsY()), new Vektor(f2.getAbsX(), f2.getAbsY()), f1.getDist(), f2.getDist(), f1.getDir(), f2.getDir());
		if((pos.sub(pos2)).length() > 0.1){
		    System.out.println("alt: " + pos);
		    System.out.println("neu: " + pos2);
		}
		*/
		Vektor pos =  Geometrie.getPosition2(new Vektor (f1.getAbsX(), f1.getAbsY()), new Vektor(f2.getAbsX(), f2.getAbsY()), f1.getDist(), f2.getDist(), f1.getDir(), f2.getDir());
		
		xPlayer = pos.getX();
		yPlayer = pos.getY();				
		viewDirection = Geometrie.getViewDirection(pos, f1);
		bodyDirection = Geometrie.normAngle(viewDirection - headDirection); 
		
		speed = amountOfSpeed;
		Vektor speedRelBody = new Vektor(speed, 0).turn(directionOfSpeed + headDirection);
		speedXrelBody = speedRelBody.getX();
		speedXrelBody = speedRelBody.getY();
		Vektor speedAbs = speedRelBody.turn(-bodyDirection);		
		/*System.out.println("xPlayer: " + xPlayer);
		System.out.println("yPlayer: " + yPlayer);
		System.out.println("bodyDirection: " + bodyDirection); 
		System.out.println("headDirection: " + headDirection);*/
		
		//int vor = (int) bodyDirection;
		/*if (bodyDirection >= 180) bodyDirection = bodyDirection - 360;
		if (bodyDirection < -180) bodyDirection = bodyDirection + 360;*/
		//if ((int) bodyDirection != vor) System.out.println("vorher: " + vor + ", nachher:" + bodyDirection+"\n" + f1 + f2);
		if ((ball != null) && (ball.getLastSeenTime() == seeTime)) {					    
		    if (oldBall.isSpeedKnown()) lastSpeedBall = oldBall;
		    //System.out.println("Zeit: " + senseBodyTime);
		    //Position des Balls eintragen:
		    setAbsObjectPosition(pos, viewDirection, ball);
			//Und versuchen, den Speedvektor einzutragen
		    if (ball.isSeen()){
			    //Vektor vom Spieler zum Ball berechnen:
			    Vektor pbVektor = new Vektor(ball.getAbsX(), ball.getAbsY()).sub(new Vektor(xPlayer, yPlayer));			    //Vektor drehen um -dirchange			    			    
			    try{
			        //System.out.print("neu - ");			        			      	
			        //Basiert auf der Frage: 
			        //Wo waere er vor einer Runde bei konstanter speed geweesen:
			        Vektor turnedVector = pbVektor.turn(-ball.getDirChange());
			        double lengthMultiplier = 1 - ball.getDistChange() / turnedVector.length();
			        Vektor correctLengthVector = turnedVector.mul(lengthMultiplier);
			        Vektor speedVektor = 
			            pbVektor
			        	.sub(correctLengthVector)
			        	.sub(new Vektor(speedXabs, speedYabs));
			        ball.setSpeedVector(speedVektor.getX(), speedVektor.getY());
			        
			    } catch (ObjectInvisibleException oie){
			        oie.printStackTrace();//sollte nicht vorkommen, da abgefragt
			    }			  
			}
			else if ((oldBall!= null) && (oldBall.getLastSeenTime() > seeTime - 3)) {
			    double deltaX = ball.getAbsX() - oldBall.getAbsX();
			    double deltaY = ball.getAbsY() - oldBall.getAbsY(); 
			    double deltaTime = ball.getLastSeenTime() - oldBall.getLastSeenTime();
			    ball.setSpeedVector(deltaX / deltaTime, deltaY / deltaTime);			    
			}
			/*if(ball.isSpeedKnown()) System.out.println("SpeedBall: " + new Vektor(ball.getSpeedX(), ball.getSpeedY()));
			if(ball.isSpeedKnown()) System.out.println("BallPos: " + new Vektor(ball.getAbsX(), ball.getAbsY()));
			if(ball.isSpeedKnown()) System.out.println("Dist/Dir: " + new Vektor(ball.getDist(), ball.getDir()));
			try{
			if(ball.isSeen()) System.out.println("DistChange/DirChange: " + new Vektor(ball.getDistChange(), ball.getDirChange()));
			}catch(Exception ex) {}*/
		}
		
		Iterator i = players.iterator();
		while (i.hasNext()){
			Player player = (Player) i.next();
			if (((Player) player).getLastSeenTime() == seeTime)
				setAbsObjectPosition(pos, viewDirection, player);
		}
	}
	
	private void setAbsObjectPosition(Vektor pos, double viewDirection,AbsPosSettable obj){
		Vektor objPos = Geometrie.getObjectPosition(pos, viewDirection, (GameObject) obj); 
		obj.setAbsX(objPos.getX());
		obj.setAbsY(objPos.getY());
	}	
	
//****************************************************************************************
//* Getter und Setter
//****************************************************************************************

	public void setBaseFunctions(BaseFunctions baseFunctions){
		this.baseFunctions = baseFunctions;
	}

	public void setSenseBodyData(String viewWidth, String viewQuality, 
			double stamina, double effort,
			double amountOfSpeed, double directionOfSpeed,
			double headDirection,
			int kickCount, int dashCount, int turnCount, int sayCount, int turnNeckCount, 
			int catchCount, int moveCount, int changeViewCount, int armCount, int focusCount,
			int tackleCount,
			String armMovable, String armExpires,
			double armTarget_Zahl1, double armTarget_Zahl2,
			String focusTarget, String tackleExpires) {
		this.viewWidth = viewWidth;
		this.viewQuality = viewQuality;
		this.stamina = stamina;
		this.effort = effort; 
		this.amountOfSpeed = amountOfSpeed;
		this.directionOfSpeed = directionOfSpeed;
		this.headDirection = headDirection;
		//Um Ballverlust ueber timeout festzustellen
		if (this.kickCount < kickCount) lastKickTime = senseBodyTime;
		this.kickCount = kickCount;		
		this.dashCount = dashCount;
		this.turnCount = turnCount;
		this.sayCount = sayCount;
		this.turnNeckCount = turnNeckCount; 
		if (this.catchCount < catchCount) ballPossession = BALL_POSSESSION_ME;
		this.catchCount = catchCount;
		this.moveCount = moveCount;
		this.changeViewCount = changeViewCount;
		this.armCount = armCount;
		this.focusCount = focusCount;
		this.tackleCount = tackleCount;
		this.armMovable =armMovable;
		this.armExpires = armExpires;
		this.armTarget_Zahl1 = armTarget_Zahl1;
		this.armTarget_Zahl2 =armTarget_Zahl2;
		this.focusTarget = focusTarget;
		this.tackleExpires = tackleExpires;
	}


		public String getViewQuality() {
			return viewQuality;
		}


	public String getViewWidth() {
		return viewWidth;
	}
	
	public String getArmMovable() {
		return armMovable;
	}


		public String getArmExpires() {
			return armExpires;
		}
		

	public String getFocusTarget() {
		return focusTarget;
	}


	public String getTackleExpires() {
		return tackleExpires;
	}


	public double getHeadDirection() {
		return headDirection;
	}


	public float getGoalWidth() {
		return goalWidth;
	}


	public int getPlayDirection() {
		return playDirection;
	}

	/**
	 * @param f
	 */
	public void setGoalWidth(float f) {
		goalWidth = f;
	}

	/**
	 * @param i
	 */
	public void setPlayDirection(int i) {
		playDirection = i;
	}	
	

	public double getXPlayer() {
		return xPlayer;
	}


	public double getYPlayer() {
		return yPlayer;
	}


	/*public void setKickOff(boolean b) {
		kickOff = b;
	}
	
	public boolean getKickOff(){
		return kickOff;
	}*/

	public int getPlayMode(){
		return playMode;
	}
	
	public void setPlayMode(int playMode){
		this.playMode = playMode; 
	}

	public String getTeamName() {
		return teamName;
	}
	
	public void setSeeTime(int time){
		this.seeTime = time;
	}

	public int getSeeTime(){
		return this.seeTime;
	}

	public void setSenseBodyTime(int time){
		this.senseBodyTime = time;
	}

	public int getSenseBodyTime(){
		return this.senseBodyTime;
	}
	
	public void setHearTime(int time){
		this.hearTime = time;
	}

	public int getHearTime(){
		return this.hearTime;
	}


	public int getDashCount() {
		return dashCount;
	}

	/**
	 * @return Die Uniformnummer des Agenten
	 */
	public int getUnum() {
		return unum;
	}

	/**
	 * @param i Die Uniformnummer des Agenten
	 */
	public void setUnum(int i) {
		unum = i;
	}

	public double getBodyDirection(){
		return bodyDirection;
	}
	
	public int getTurnCount() {
		return turnCount;
	}

	/**
	 * Fuegt der Nachrichtenwarteschlange (FIFO) eine Nachricht hinzu
	 * @param m Die Nachricht, die hinzugefuegt wird 
	 */	

	
    /**
     * @return Returns the amountOfSpeed.
     */
    public double getAmountOfSpeed() {
        return amountOfSpeed;
    }
	
	
	/**Setter für die Ausgangsposition des Spieler;
	 * x-koordinate
	 * @param playerHome The xPlayerHome to set.
	 */
	public void setXPlayerHome(double playerHome) {
		this.xPlayerHome = playerHome;
	}

	/**Setter für die Ausgangsposition des Spieler;
	 * y-koordinate
	 * @param playerHome The yPlayerHome to set.
	 */
	public void setYPlayerHome(double playerHome) {
		this.yPlayerHome = playerHome;
	}
	

	/**
	 * @return Returns the x1PlayerActingField.
	 */
	public double getX1PlayerActingField() {
		return x1PlayerActingField;
	}
	
	/**
	 * @param playerActingField The x1PlayerActingField to set.
	 */
	public void setX1PlayerActingField(double playerActingField) {
		this.x1PlayerActingField = playerActingField;
	}
	/**
	 * @return Returns the x2PlayerActingField.
	 */
	public double getX2PlayerActingField() {
		return x2PlayerActingField;
	}
	/**
	 * @param playerActingField The x2PlayerActingField to set.
	 */
	public void setX2PlayerActingField(double playerActingField) {
		this.x2PlayerActingField = playerActingField;
	}
	/**
	 * @return Returns the y1PlayerActingField.
	 */
	public double getY1PlayerActingField() {
		return y1PlayerActingField;
	}
	/**
	 * @param playerActingField The y1PlayerActingField to set.
	 */
	public void setY1PlayerActingField(double playerActingField) {
		this.y1PlayerActingField = playerActingField;
	}
	/**
	 * @return Returns the y2PlayerActingField.
	 */
	public double getY2PlayerActingField() {
		return y2PlayerActingField;
	}
	/**
	 * @param playerActingField The y2PlayerActingField to set.
	 */
	public void setY2PlayerActingField(double playerActingField) {
		this.y2PlayerActingField = playerActingField;
	}
	
	/**
	 * @return Returns the xPlayerStart.
	 */
	public double getXPlayerStart() {
		return xPlayerStart;
	}
	/**
	 * @param playerStart The xPlayerStart to set.
	 */
	public void setXPlayerStart(double playerStart) {
		xPlayerStart = playerStart;
	}
	/**
	 * @return Returns the yPlayerStart.
	 */
	public double getYPlayerStart() {
		return yPlayerStart;
	}
	/**
	 * @param playerStart The yPlayerStart to set.
	 */
	public void setYPlayerStart(double playerStart) {
		yPlayerStart = playerStart;
	}	
	
	/**
	 * @return Returns the playerAlive.
	 */
	public boolean isPlayerAlive() {
		return playerAlive;
	}
	
	/**
	 * @return Returns the xPlayerHome.
	 */
	public double getXPlayerHome() {
		return xPlayerHome;
	}
	
	/**
	 * @return Returns the yPlayerHome.
	 */
	public double getYPlayerHome() {
		return yPlayerHome;
	}
	
	/**
	 * @param playerAlive The playerAlive to set.
	 */
	public void setPlayerAlive(boolean playerAlive) {
		this.playerAlive = playerAlive;
	}
	
	/**
	 * @return Returns the x1PlayerFreeKickField.
	 */
	public double getX1PlayerFreeKickField() {
		return x1PlayerFreeKickField;
	}
	/**
	 * @param playerFreeKickField The x1PlayerFreeKickField to set.
	 */
	public void setX1PlayerFreeKickField(double playerFreeKickField) {
		x1PlayerFreeKickField = playerFreeKickField;
	}
	/**
	 * @return Returns the x2PlayerFreeKickField.
	 */
	public double getX2PlayerFreeKickField() {
		return x2PlayerFreeKickField;
	}
	/**
	 * @param playerFreeKickField The x2PlayerFreeKickField to set.
	 */
	public void setX2PlayerFreeKickField(double playerFreeKickField) {
		x2PlayerFreeKickField = playerFreeKickField;
	}
	/**
	 * @return Returns the y1PlayerFreeKickField.
	 */
	public double getY1PlayerFreeKickField() {
		return y1PlayerFreeKickField;
	}
	/**
	 * @param playerFreeKickField The y1PlayerFreeKickField to set.
	 */
	public void setY1PlayerFreeKickField(double playerFreeKickField) {
		y1PlayerFreeKickField = playerFreeKickField;
	}
	/**
	 * @return Returns the y2PlayerFreeKickField.
	 */
	public double getY2PlayerFreeKickField() {
		return y2PlayerFreeKickField;
	}
	/**
	 * @param playerFreeKickField The y2PlayerFreeKickField to set.
	 */
	public void setY2PlayerFreeKickField(double playerFreeKickField) {
		y2PlayerFreeKickField = playerFreeKickField;
	}
	
	
	public float getKickableMarginDeltaMax() {
		return kickableMarginDeltaMax;
	}
	
	public float getKickableMarginDeltaMin() {
		return kickableMarginDeltaMin;
	}
	
	
	public void setKickableMarginDeltaMax(float kickableMarginDeltaMax) {
		this.kickableMarginDeltaMax = kickableMarginDeltaMax;
	}
	
	public void setKickableMarginDeltaMin(float kickableMarginDeltaMin) {
		this.kickableMarginDeltaMin = kickableMarginDeltaMin;
	}
	
	public int getExpectedPlayMode() {
		return expectedPlayMode;
	}
	
	public void setExpectedPlayMode(int expectedPlayMode) {
		this.expectedPlayMode = expectedPlayMode;
	}
	
	public void setPassMessage(PassMessage passMessage) {
		this.passMessage = passMessage;
	}
	
	
	public PassMessage getPassMessage() {
		return passMessage;
	}
	
	/**
	 * @return Returns the personToCover.
	 */
	public int getPersonToCover() {
		return personToCover;
	}
	/**
	 * @param personToCover The personToCover to set.
	 */
	public void setPersonToCover(int personToCover) {
		this.personToCover = personToCover;
	}
	
    /**
     * @return Returns the ballPossession.
     */
    public int getBallPossession() {
        return ballPossession;
    }
    /**
     * @param ballPossession The ballPossession to set.
     */
    public void setBallPossession(int ballPossession) {
        this.ballPossession = ballPossession;        
        if (ballPossession == BALL_POSSESSION_ME) lastKickTime = senseBodyTime;         
    }
    
	/**
	 * @param catchableAreaL The catchableAreaL to set.
	 */
	public void setCatchableAreaL(float catchableAreaL) {
		this.catchableAreaL = catchableAreaL;
	}
	/**
	 * @param catchableAreaW The catchableAreaW to set.
	 */
	public void setCatchableAreaW(float catchableAreaW) {
		this.catchableAreaW = catchableAreaW;
	}
	
	/**
	 * @param role The role to set.
	 */
	public void setRole(int role) {
		this.role = role;
	}

///***********************************************************************************
/* Zusaetzliche Informationen Anfang*/
public double angleToObject(double x, double y){
		double deltaX = x - getXPlayer();
		double deltaY = y - getYPlayer();
	//	System.out.println("deltaX: " + deltaX);
	//	System.out.println("deltaY: " + deltaY);
		
		double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
		double alpha = 0;
		//Richtung ausrechnen
		if ((deltaX >= 0) && (deltaY <= 0)) alpha = - Math.asin(- deltaY / dist) / (Math.PI * 2) * 360;
		else if ((deltaX >= 0) && (deltaY > 0)) alpha =  Math.asin(deltaY / dist) / (Math.PI * 2) * 360; 
		else if ((deltaX < 0) && (deltaY <= 0)) alpha = -180 + Math.asin(- deltaY / dist) / (Math.PI * 2) * 360;
		else if ((deltaX < 0) && (deltaY > 0)) alpha = 180 - Math.asin(deltaY / dist) / (Math.PI * 2) * 360;
		else System.err.println("angleToObjekt poebelt:Das darf nicht ausgefuehrt werden!");
		return alpha;
	}

	public Vektor deckenPosition(Ball ball, Player player){
		Vektor a = new Vektor(player.getAbsX(), player.getAbsY());
		Vektor b = new Vektor((ball.getAbsX()-player.getAbsX()), (ball.getAbsY() - player.getAbsY()));
		Vektor c = new Vektor(xPlayer, yPlayer);
		double t = (c.sub(a)).mul(b);
		t = t / b.length();
		Vektor x = a.add(b.mul(t));
		return x;		
	}
	
	
	/************************************************************************** 
	 * Laeuft einen Schritt in eine angegebene Richtung.
	 * Beinhaltet eine Toleranz von 1 Grad, um unnoetige turn-Aufrufe zu verhindern
	 * @param dir Die Richtung, in die gelaufen werden soll. Bezug: absolut
	 * @param power Die Power, mit der dash aufgerufen wird
	 **************************************************************************/
	public void dashTo(double dir, int power){
	    dir = dir - bodyDirection;
		if (dir >= 180) dir = dir - 360; 
		if (dir < -180) dir = dir + 360;	
		if (Math.abs(dir) > 20){
		    baseFunctions.turn(dir);
		   
		} 
		baseFunctions.dash(power);
		
	}
	
	
	/************************************************************************** 
	 * Laeuft einen Schritt in eine angegebene Richtung.
	 * Beinhaltet eine Toleranz von 1 Grad, um unnoetige turn-Aufrufe zu verhindern
	 * @param dir Die Richtung, in die gelaufen werden soll. Bezug: absolut
	 * @param power Die Power, mit der dash aufgerufen wird
	 **************************************************************************/
	public void dashToSeeingBall(double dir, int power){
		baseFunctions.changeView("wide", "high");
		if((ball == null) || (ball.getLastSeenTime() + 2 <  seeTime)){
			if(!lookForBall(2)) return;	
		}
		double dirDiffBall = Math.abs(angleToObject(ball.getAbsX(), ball.getAbsY()) - dir);
		if (dirDiffBall < 135){
			dir = Geometrie.normAngle(dir - bodyDirection);
		}else {
			dir = Geometrie.normAngle(dir - bodyDirection + 180);
			power = power * -1;
		}
		
		if (Math.abs(dir) > 20){
		    baseFunctions.turnNeckWithoutWaiting(ball.getDir()-dir);
			baseFunctions.turn(dir);
		} else{
		    baseFunctions.turnNeckWithoutWaiting(ball.getDir());
		    baseFunctions.dash(power);
		}
	baseFunctions.changeView("normal", "high");
	}
	
	public boolean lookForBall(int memoryTime){
		int i = 0;
		baseFunctions.changeView("wide", "high");
	    while ((checkOuterState() == true) 
		        && ((ball == null) || (ball.getLastSeenTime() + memoryTime <  seeTime))
		        && i < 2){
	    	if (i == 0) baseFunctions.turnNeckRelToBody(-90);
	    	else baseFunctions.turnNeckRelToBody(90);
			i++;
		}
	    baseFunctions.changeViewWithoutWaiting("normal", "high");
	    if ((ball == null) || ball.getLastSeenTime() + memoryTime <  seeTime) 
		    return false;
		else
		    return true;
	}
	
	/*******************************************************
	 * Laeuft mit einer bestimmten Geschwindigkeit zu
	 * einer absolut angegebenen Position
	 * @param homeX X-Zielposition (absolut)
	 * @param homeY Y-Zielposition (absolut)
	 * @param power Die Power, mit der dash aufgerufen wird
	 *******************************************************/
	public void runToXY(double homeX, double homeY, int power){
		while (checkOuterState() && 
				(distTo(homeX, homeY) > 1) &&
				checkPositionInActingField(homeX, homeY)){
			double alpha = angleToObject(homeX,homeY);
			//System.out.println("Ich mach gerade runToXY nach (" + homeX + ", " + homeY+ "angleToObject " + alpha + ")\n"
			 //       +"ich stehe auf(" + xPlayer + ", " + yPlayer + "), bodyDirection " + bodyDirection + ": ");
			dashTo(alpha, power);
		}
	}
	
	public void runCorrectToXY(double x, double y, int power){
	    int pow = power;
	    while (checkOuterState() && 
	    		(distTo(x,y) > kickableMarginDeltaMax) &&
				checkPositionInActingField(x,y)){
			if (distTo(x,y) < 1.5) {
				pow = (int) (distTo(x,y)*30);
			}
			else pow = power;	
			double alpha = angleToObject(x,y);
			dashTo(alpha, pow);
		}
	}
	/*******************************************************
	 * Laeuft mit einer bestimmten Geschwindigkeit zum
	 * Ball. Das besondere hierdran ist, dass der Spieler abbremst,
	 * wenn er dem Ball zu nahe kommt, um nicht ständig über den Ball
	 * zu dashen. Die Art des Abbremsen ist hier empirisch ermittelt
	 * @param power Die Power, mit der dash aufgerufen wird
	 *******************************************************/
	public boolean runToBall(int power, int kickTimeOut){
	    searchBall(1, kickTimeOut);
		int pow = power;
		// um die Renngeschwindigkeit an die fehlende Distanz anzupassen
		while (
		        checkOuterState() 
		        && (ball.getDist() > kickableMarginDeltaMax)
		        && !kickTimeOut(kickTimeOut)
				&& checkBallInActingField()){
			
			/*if (ball.getDist() < 1) {
				pow = (int) (ball.getDist()*100);
				System.out.println(senseBodyTime + ": power nur " + pow);
			}
			else*/ pow = power;	
			double alpha = ball.getDir()+ bodyDirection + headDirection;
			dashTo(alpha, pow);
			while(!kickTimeOut(kickTimeOut) && !searchBall(1, kickTimeOut) && checkOuterState());
		}
		if (kickTimeOut(kickTimeOut) || !checkOuterState){ 
		    return false;
		}
		else{
		    return true;
		}
		    
	}
	
	
	
	
	/****************************************************************
	 * Gibt das Objekt des Spielers mit der uebergebenen Trikotnummer
	 * zurueck, falls es bekannt ist. 
	 * @param unum Die Trikotnummer des Spielers
	 * @param team entweder Player.TEAM_WE oder Player.TEAM_OPPONENT
	 * @return Ein Spielerobjekt, falls der Spieler irgendwann 
	 * gesehen wurde, sonst null
	 **************************************************************/
	public Player getPlayer(int unum, int team){
		Iterator i = players.iterator();
		while(i.hasNext()){
			Player p = (Player) i.next();
			try{	
				System.out.println(senseBodyTime + ": getPlayer " + p);
				if ((p.getUnum() == unum) && (team == p.getTeam())) return p;
			}
			catch(ObjectInvisibleException e){
				/*do nothing*/ 
			}
		}
		return null;
	}

	
	
	public boolean searchBall(int memoryTime){		
	    int i = 0;
	    while ((checkOuterState() == true) 
		        && ((ball == null) || (ball.getLastSeenTime() + memoryTime <  seeTime))
		        && i < 7){
	    	baseFunctions.turnNeckRelToBodyWithoutWaiting(0);
			baseFunctions.turn(45);
			i++;
		}
		if ((ball == null) || ball.getLastSeenTime() + memoryTime <  seeTime) 
		    return false;
		else
		    return true;
	}
	
	
	public boolean searchBall(int memoryTime, int kickTimeOut){
		while (checkOuterState() 
		        && ((ball == null) || (ball.getLastSeenTime() + memoryTime <  seeTime))
				&& !kickTimeOut(kickTimeOut)){
			baseFunctions.turn(45);
		}
		if (kickTimeOut(kickTimeOut)) 
		    return false;
		else{
		    return true;
		}
	}		
	
	
	/*******************************************************
	 * Dreht sich zum Ball
	 * Benoetigt evtl. mehrere Runden, um den Ball zu finden
	 * und evtl. eine, um einen turn-befehl zu verschicken
	 *******************************************************/
	public boolean turnToBall(int kickTimeOut){
	    if(searchBall(0, kickTimeOut) == false)
	        return false;
	    if (Math.abs(ball.getDir()) > 10) baseFunctions.turn(ball.getDir());
	    return true;
	}
	
	/***************************************
	 * Distanz von der X- & Y-Position des Spieler 
	 * zu einem Punkt
	 * @param x X-Koordinate
	 * @param y Y-Koordinate
	 * @return Distanz
	 ****************************************/
	public double distTo(double x, double y){
		double deltaX = x - xPlayer;
		double deltaY = y - yPlayer;
		return Math.sqrt(deltaX*deltaX + deltaY*deltaY);
	}
	
	
	/***************************************************
	 * Dreht sich solange um die eigene Achse, bis der
	 * Spieler mit der Trikotnummer und dem angegebenen
	 *  Team gefunden wurde
	 * @param unum die Trikotnummer des zu suchenden 
	 * Spielers
	 * @param team entweder Player.TEAM_WE oder Player.TEAM_OPPONENT
	 * @return Das Spielerobjekt
	 ****************************************************/
	public Player searchPlayer(int unum, int team) {
		Player p = getPlayer(unum, team);
		int i = 0;
		while ((checkOuterState() == true) 
		        && ((p == null) || (p.getLastSeenTime() < seeTime))
		        && (i < 7)) {
			baseFunctions.turn(45);
			p = getPlayer(unum, team);
			i++;
		}
		return p;
	}
	
	/********************************************************
	 * Laeuft zuerst an den Spieler heran, wenn zwei Meter vom
	 * Spieler entfernt: Richtungsaenderung und zwischen Ball und 
	 * Spieler laufen
	 * @param unum Trikotnummer des zu deckenden Spielers
	 * @param team entwerder Player.TEAM_WE oder 
	 * Player.TEAM_OPPONENT
	 ********************************************************/
	public void runCloseToPlayer(int unum, int team) {
		//Nah an den Spieler heranlaufen
		if(unum == -1) return;
		Player p = getPlayer(unum, team);
		if(p == null){
			personToCover = -1;
			return;
		}
		if((p == null) || (p.getLastSeenTime() < seeTime - 10)) {
			p = searchPlayer(unum, team);
			if(p == null 
					|| (p.getLastSeenTime() < seeTime - 10)
					|| !checkPositionInActingField(p.getAbsX(), p.getAbsY())){
				personToCover = -1;
				return;
			}
		}
		while(	(checkOuterState() == true) 
				&& (unum == personToCover)
				&& (distTo(p.getAbsX(), p.getAbsY()) > 5)){						
			dashTo(angleToObject(p.getAbsX(), (p.getAbsY())), 100);
			p = getPlayer(unum, team);
			if(p == null 
					|| (p.getLastSeenTime() != seeTime)
					|| !checkPositionInActingField(p.getAbsX(), p.getAbsY())){
				personToCover = -1;
				return;
			}
		}
		if ((unum != personToCover) || !checkOuterState()) {
			return;
		}
		//zwischen Spieler und Ball laufen
		
		if (!searchBall(10)) return;
		Vektor vSpieler = new Vektor(p.getAbsX(), p.getAbsY());
		Vektor vBall = new Vektor(ball.getAbsX(), ball.getAbsY());
		Vektor spielerZuBall = vBall.sub(vSpieler);
		//2 ist der Abstand vom Spieler
		Vektor endPunkt = vSpieler.add(spielerZuBall.div(spielerZuBall.length()).mul(2));
		double endX = endPunkt.getX();
		double endY = endPunkt.getY();
		if (!checkPositionInActingField(endX, endY)){
			personToCover = -1;
			return;
		}
		while (
				checkOuterState() 
				&& (distTo(endX, endY) > 1)
				&& (unum == personToCover)){
			double alpha = angleToObject(endX, endY);
			dashTo(alpha, 100);
		}		
		baseFunctions.turnNeckRelToBodyWithoutWaiting(0);
		baseFunctions.turnAbs(angleToObject(ball.getAbsX(), ball.getAbsY()));
	}
	
	public void cover(int unum) {		
	    while (
				checkOuterState() 
				&& (unum == personToCover)
				&& (checkPlayerInActingField(0))				
				&& (ball.getDist() >= 10)){
			if(unum == -1) return;
	    	runCloseToPlayer(unum, Player.TEAM_OPPONENT);						
			boolean isPlayerNotNull;
			boolean isPlayerBehindUs; 
			boolean isPlayerTouched;
			boolean isBallRelevant; 
			boolean isBallBeforeUs;
			do {
			    Iterator i = players.iterator();
			    Player p = null;
			    //Schauen, ob Spieler gef?hlt. Falls nicht: p null oder p irgendein Spieler
				while (i.hasNext() && (p == null || (p.getMode()!= Player.TOUCHED_PLAYER))){ 
				    p = (Player) i.next(); 
				}
				if (p == null) break;			
				isPlayerBehindUs = (Math.abs(p.getDir()) >= 180 - 45); 
				isPlayerTouched = (p.getMode() == Player.TOUCHED_PLAYER);
				isBallRelevant = (ball.getLastSeenTime() == seeTime); 
				isBallBeforeUs = (Math.abs(ball.getDir()) <= 10);
				Thread.yield();
			}
			//Solange irgendein Player da und der hinter uns ist und der gef?hlt wurde 
			//und der Ball gesehen wurde und vor uns ist
			while(
				isPlayerBehindUs 
				&& isPlayerTouched
				&& isBallRelevant 
				&& isBallBeforeUs
				&& (unum == personToCover)
				&& (ball.getDist() >= 10));						
		}
	}
	
	/****************************************************
	 * dribbelt zu einer bestimmten Position.
	 * passt den Ball, wenn ein Mitspieler frei steht
	 * ansonsten schiesst er aufs Tor
	 * 
	 * countOpponentsInCone ist eine schöne Stellschraube
	 * @param x X-Koordinate
	 * @param y Y-Koordinate
	 ****************************************************/
	public void dribbleTo(double x, double y){
		baseFunctions.changeViewWithoutWaiting("narrow", "high");
		while (checkOuterState() && 
				checkBallInActingField() &&
				checkPlayerInActingField(5) &&
				(countOpponentsInCone(10, 45) < 1) &&
				(countOpponentsInCone(20, 10) < 5) &&
				//(countOpponentsInCone(2, 180) < 1) &&
				(distTo(x, y) > 5) &&
				!kickTimeOut(DRIBBLE_TIMEOUT)
				){
			if ((ball == null) || (ball.getLastSeenTime()<seeTime)) {
			    searchBall(0, DRIBBLE_TIMEOUT);
			}
			while (
			        checkOuterState() 
			        && (ball.getDist() > 1)
			    	&& !kickTimeOut(DRIBBLE_TIMEOUT))
				runToBall(100, DRIBBLE_TIMEOUT);
			if((distTo(x, y) > 5) && !kickTimeOut(DRIBBLE_TIMEOUT)){
			    baseFunctions.kick(15, angleToObject(x, y) - bodyDirection);
			}
		}
		baseFunctions.changeViewWithoutWaiting("normal", "high");		
		if(kickTimeOut(DRIBBLE_TIMEOUT)) return;
		//stopBallWithoutNonsense();
		if(kickTimeOut(DRIBBLE_TIMEOUT)) return;
		try {
			Player freePlayer = findFreePlayer();
		    passToFreePlayer(freePlayer);
		} catch(NoFreePlayerException e) {
			kickToGoal();
			ballPossession = BALL_POSSESSION_OPPONENT;
		}
		
	}

	/**
	 * Sucht freien Spieler, beinhaltet Kicktimeout(Passtimeout)
	 * @return freier Spieler oder null
	 */
	public Player findFreePlayer(){
	    Player freePlayer = null;
		for (float i=0.5f; 
				(i<4.5) 
				&& checkOuterState() 
				&& (freePlayer==null)
				&& !kickTimeOut(PASS_TIMEOUT); 
				i+=0.5){		    
		    int turn = (int)i * 45;
		    if (i%1 != 0) turn *= -1;		
		    baseFunctions.turnAbs(turn);
		    freePlayer = getFreePlayer();
		}
		return freePlayer;
	}
	
	/**************************************************************************
	 * sucht den Ball, rennt zu ihm hin und spielt ihn zu einem Mitspieler
	 **************************************************************************/
	public void passToFreePlayer(Player freePlayer) throws NoFreePlayerException {
		baseFunctions.turnNeckRelToBodyWithoutWaiting(0);
		if(!runToBall(STAMINA_NEUTRAL_SPEED, PASS_TIMEOUT)){
		    return;
		}
		// sucht den Bereich in Richtung Tor eher ab, um einen Spieler zu finden
		
		if (kickTimeOut(PASS_TIMEOUT)) System.err.println("Falscher fehler beim Passen2");
		if (freePlayer == null) throw new NoFreePlayerException();

		Vektor passPosition = calculatePassPosition(freePlayer);		    
	    while (senseBodyTime % 2 == 0) Thread.yield();
		int timer = senseBodyTime;
	    try{		        
	        baseFunctions.sayPassTo(
	            freePlayer.getUnum(), 
	            (int) Math.round(passPosition.getX()),
	            (int) Math.round(passPosition.getY()),
	            (int) angleToObject(passPosition.getX(), passPosition.getY())		       
	        );		        	        
	    } catch (ObjectInvisibleException e){
	        e.printStackTrace();
	    }
	    
	    
	    double kickPower = freePlayer.getDist()/0.027;
	    
	    if (kickPower > 100) kickPower=100;
	    baseFunctions.kick(kickPower,angleToObject(passPosition.getX(), passPosition.getY())
	            		- getBodyDirection());		    
        ballPossession = BALL_POSSESSION_WE;		        

	}
	
	/****************************************************************
	 * Berechnet die "optimale" Position, zu der gepasst werden muss,
	 * um einem Spieler des eigenen Teams den Ball zu geben
	 * @param friendlyUnum Die Trikotnummer des angepassten Spuielers
	 * @return Ein Vektor mi den Koordinaten des Zielpunktes
	 ***************************************************************/
	public Vektor calculatePassPosition(int friendlyUnum){
		Player friendly = getPlayer(friendlyUnum, Player.TEAM_WE);
		return calculatePassPosition(friendly);
	}
	
	
	/****************************************************************
	 * Berechnet die "optimale" Position, zu der gepasst werden muss,
	 * um einem Spieler des eigenen Teams den Ball zu geben
	 * @param friendly Das Spielerobjekt des anzuspielenden Spielers
	 * @return Ein Vektor mi den Koordinaten des Zielpunktes
	 ***************************************************************/
	public Vektor calculatePassPosition(Player friendly){
		if (friendly == null) return null;
		Vektor destination = new Vektor(friendly.getAbsX(), friendly.getAbsY()).add(new Vektor(xPlayer - friendly.getAbsX(), yPlayer - friendly.getAbsY()).mul(1d/8d)) ;
		//Auf den Spieler zudrehen, um Gegenspieler zu sehen:
		//Auskommentieren ist riskant aber *wesentlich* schneller, da die 
		//Methode oft aufgerufen wird:
		//sender.turn(friendly.getDir());
		Iterator i = players.iterator();
		int opponentCount = 0;
		Vektor summand = new Vektor(0, 0);
		while (i.hasNext()){
			Player p = (Player) i.next();
			if (p.getTeam() == Player.TEAM_OPPONENT){
				Vektor opponent = new Vektor(friendly.getAbsX() - p.getAbsX(), friendly.getAbsY() - p.getAbsY());
				//Die Multiplikation stellt einen Faktor dar, um dem der Vektor gestreckt wird.
				summand = summand.add(opponent.div(opponent.length() * opponent.length()).mul(100));
				opponentCount++;
			}
		}
		//if(Math.abs(summand.getX()) + Math.abs(summand.getY()) != 0) destination = destination.add(summand.div(opponentCount));
		// 48 ist hier die kritische Grenze
		if (destination.getX() > 48) destination.setX(48);
		if (destination.getX() < -48) destination.setX(-48);
		if (destination.getY() > 30) destination.setX(30);
		if (destination.getY() < -30) destination.setX(-30);	
		return destination;
	}

	/*********************************************************************
	 * Diese Methode ermittelt einen frei stehenden Spieler anhand
	 * der minimalen Differenz von calculatePassPosition und dem Spieler
	 * in unserem Sichtfeld 
	 * @return 
	 * Spieler, zu dem gepasst werden soll<br>
	 * null, falls trotz Suchens kein Spieler gesehen wurde.
	 *********************************************************************/
	public Player getFreePlayer(){
		//long startTime = System.currentTimeMillis();
		Iterator i = players.iterator();
		double minDistToPlayer=Double.POSITIVE_INFINITY;
		Player minDistPlayer = null;
		while(i.hasNext()){
			Player p = (Player) i.next();		
			//Die mit unserem geilen Trikot und die, die wir sehen
			if (Player.TEAM_WE == p.getTeam() && (p.getMode() == Player.UNUM_PLAYER) && (p.getLastSeenTime() == seeTime)){
				
				Vektor freePlayer = new Vektor(p.getAbsX(), p.getAbsY());
				//ist der Spieler gedeckt?
				boolean covered = false;				
				/* Kein Gegenspieler darf innerhalb eines bestimmten Winkels vor 
				 * unserem Spieler stehen.
				 */
				Iterator j = players.iterator();
				while (!covered && j.hasNext()){
					Player o = (Player) j.next();
					if ((o.getTeam() != Player.TEAM_WE) 
						&& (o.getDist() < p.getDist()) 
						&& (Math.abs(o.getDir() - p.getDir()) < 10)
						&& o.getLastSeenTime() == seeTime){
						covered = true;						
					}
				}										
				
				if(!covered){
					Vektor passPosition = calculatePassPosition(p); 
					double distToPlayer = distTo(passPosition.getX(), passPosition.getY());
					if (distToPlayer <= minDistToPlayer) {
						minDistToPlayer = distToPlayer;
						minDistPlayer = p;
					}
				}
			}		
		}		
		// System.out.println("Methode getFreePlayer hat " + (System.currentTimeMillis() - startTime) + " ms benoetigt.");
		return minDistPlayer;
	}
	
	/***************************************************
	 * Diese Methode prüft, ob sich ein Spieler innerhalb eines 
	 * vorgegeben Vierecks befindet. dabei ist x1,y1 die linke 
	 * obere Ecke und x2,y2 die rechte untere Ecke
	 * @param x1 x1-Kooredinate
	 * @param y1 y1-Kooredinate
	 * @param x2 x2-Kooredinate
	 * @param y2 y2-Kooredinate
	 * @return true wenn der Spieler im Feld ist, ansonsten false
	 **************************************************/
	public boolean checkPlayerPosition(double x1,double y1, double x2, double y2){
		/*if ((xPlayer < x1) || (xPlayer > x2) || (yPlayer < y1) || (yPlayer > y2))
			return false;
		else
			return true;*/
		return checkPositionInArea(xPlayer, yPlayer, x1, y1, x2, y2);
	}
	
	public boolean checkPositionInArea(double x, double y, double x1, double y1, double x2, double y2){
		if ((x < x1) || (x > x2) || (y < y1) || (y > y2))
			return false;
		else
			return true;
	}
	
	public boolean checkPositionInActingField(double x, double y){		
		return checkPositionInArea(
				x,
				y,
				getX1PlayerActingField(),
				getY1PlayerActingField(),
				getX2PlayerActingField(),
				getY2PlayerActingField());
	}
	
	public boolean checkBallPosition(double x1,double y1, double x2, double y2){ 
		//System.out.println("checkBallPosition" + (x1) + (x2) + (y1) + (y2));
		//System.out.println("checkBallPosition" + (getBall().getAbsX()) + (getBall().getAbsY()));
		//TODO timeout hier ueberdenken
	    if ((getBall() == null) || (getBall().getLastSeenTime() < seeTime)) searchBall(0);
		if ((getBall().getAbsX() < x1) || (getBall().getAbsX() > x2) || (getBall().getAbsY() < y1) || (getBall().getAbsY() > y2))
			return false;
		else
			return true;
	}
	
	/*************************************************************
	 * Methode, die überprüft, ob sich der Spieler in seinem zuständigen
	 * Bereich befindet. Dabei sind getX1PlayerActingField... die Eck-
	 * punkte des Spielfeldes
	 * * @param dist Gibt den Abstand zur Grenze zum ActingFields
	 * @return true wenn der Spieler im Feld ist, ansonsten false
	 *************************************************************/
	public boolean checkPlayerInActingField(int dist){
		return checkPlayerPosition(getX1PlayerActingField()+dist,
									getY1PlayerActingField()+dist,
									getX2PlayerActingField()-dist,
									getY2PlayerActingField()-dist);
	}
	
	/**************************************************************
	 * Prueft ob der Spieler den FREE_KICK ausführen soll, indem ueberprueft
	 * wird, ob sich der Ball in dessen Freistossbereich befindet.
	 * @return  
	 * true, wenn der Ball im Freistossbereichs des Spielers liegt<br>
	 * ansonsten false
	 **************************************************************/
	public boolean checkBallInFreeKickField(){
		return checkBallPosition(getX1PlayerFreeKickField(),
									getY1PlayerFreeKickField(),
									getX2PlayerFreeKickField(),
									getY2PlayerFreeKickField());
	}
	

	/**************************************************************
	 * Prueft ob der Ball sich im Aktionsbereich des Spielers befindet.
	 * Sucht ggf. zuerst den Ball.
	 * @return 
	 * true, wenn der Ball im Aktionsbereich des Spielers liegt,
	 * ansonsten false
	 **************************************************************/
	public boolean checkBallInActingField(){
		return checkBallPosition(getX1PlayerActingField(),
								 getY1PlayerActingField(),
								 getX2PlayerActingField(),
								 getY2PlayerActingField());
	}
	
	public int checkBallPossession(){
		return ballPossession;
	}
	
	/**********************************************************
	 * Methode für die einzelnen Spielmodi und Spielereignisse
	 * wenn kein eingreifen aufgrund der aeußere Zustaende
	 * erforderlich ist, ist der Rückgabewert true  
	 **********************************************************/
	public boolean checkOuterState(){
		int playMode = getPlayMode();
		if (playMode == expectedPlayMode) return true;
		else return false;

	}
	
	public boolean checkInnerState(){
		if (checkPlayerInActingField(0) == true)
			return true;
		else
			return false;
	}
	
	
	/****************************************
	 * wartet auf die naechste See-Nachricht
	 ****************************************/
	public void waitForNextSee(){
	    int now = seeTime;
	    while((seeTime == now) && checkOuterState()) Thread.yield();
	}

    /**
     * @return Returns the kickCount.
     */
    public int getKickCount() {
        return kickCount;
    }
    /**
     * @return Returns the tackleCount.
     */
    public int getTackleCount() {
        return tackleCount;
    }
    double startTime;

    
    public Flag getOpponentGoal(){
   		if (playDirection == 1) {
			return getFlag("g r");
		} else{
			return getFlag("g l");
		} 
   	}
   	
   	public Flag getOurGoal(){
   		if (playDirection == -1) {
			return getFlag("g r");
		} else{
			return getFlag("g l");
		} 
   	}
   	
    public double calculateGoalKickDir(){ 
		double goalTopDir;
		double goalBottomDir;
		double minDir, maxDir;
		
		if (playDirection == 1) {
			//sender.turn(angleToObject(getFlag("g r").getAbsX(), getFlag("g r").getAbsY()));
			goalTopDir = angleToObject(getFlag("g r t").getAbsX(), getFlag("g r t").getAbsY());
			goalBottomDir = angleToObject(getFlag("g r b").getAbsX(), getFlag("g r b").getAbsY());		
		} else{
			//sender.turn(angleToObject(getFlag("g l").getAbsX(), getFlag("g l").getAbsY()));
			goalTopDir = angleToObject(getFlag("g l t").getAbsX(), getFlag("g l t").getAbsY());
			goalBottomDir = angleToObject(getFlag("g l b").getAbsX(), getFlag("g l b").getAbsY());
		}
		if (goalTopDir > goalBottomDir) {
			minDir = goalBottomDir;
			maxDir = goalTopDir;
		} else {
			minDir = goalTopDir;
			maxDir = goalBottomDir;	
		}
		
		Vector opponentPlayer = new Vector();
		Iterator i = players.iterator();
	
		while(i.hasNext()){
			Player p = (Player) i.next();		
			//Die mit dem anderen doofen Trikot und die, die wir sehen...	
			if (Player.TEAM_OPPONENT == p.getTeam() && 
				(p.getLastSeenTime() == seeTime) &&
				(angleToObject(p.getAbsX(), p.getAbsY()) <= maxDir) &&
				(angleToObject(p.getAbsX(), p.getAbsY()) >= minDir)){
					opponentPlayer.addElement(new Double(angleToObject(p.getAbsX(), p.getAbsY())));
			}
		}
		//... und die Torfosten
		opponentPlayer.addElement(new Double(minDir));
		opponentPlayer.addElement(new Double(maxDir));
		// Das Array dann nach den Directions sortieren.
		Collections.sort(opponentPlayer);
		
		
		//Den maximalen Winkel berechnen.
		double maxDeltaDir = -999; 
		double kickDir=-999;	
		double memADir=-999; 
		double memBDir=-999;
		boolean flag = true;
		Iterator j = opponentPlayer.iterator();
		int counter=0;
			while (j.hasNext()) {
				if (counter == 0) memADir = Double.parseDouble(j.next().toString());
				if (j.hasNext()) memBDir = Double.parseDouble(j.next().toString());
					if ((memBDir-memADir)  > maxDeltaDir) {
						maxDeltaDir = (memBDir-memADir);
						kickDir = memADir + (Math.abs(maxDeltaDir)/2);
					}
				counter++;
				memADir = memBDir;
		}
		return kickDir;
	}
   	    
   	
   	    public double calculateGoalKickDeltaMax(){ 
   			double goalTopDir;
   			double goalBottomDir;
   			double minDir, maxDir;
   			
   			if (playDirection == 1) {
   				//sender.turn(angleToObject(getFlag("g r").getAbsX(), getFlag("g r").getAbsY()));
   				goalTopDir = angleToObject(getFlag("g r t").getAbsX(), getFlag("g r t").getAbsY());
   				goalBottomDir = angleToObject(getFlag("g r b").getAbsX(), getFlag("g r b").getAbsY());		
   			} else{
   				//sender.turn(angleToObject(getFlag("g l").getAbsX(), getFlag("g l").getAbsY()));
   				goalTopDir = angleToObject(getFlag("g l t").getAbsX(), getFlag("g l t").getAbsY());
   				goalBottomDir = angleToObject(getFlag("g l b").getAbsX(), getFlag("g l b").getAbsY());
   			}
   			if (goalTopDir > goalBottomDir) {
   				minDir = goalBottomDir;
   				maxDir = goalTopDir;
   			} else {
   				minDir = goalTopDir;
   				maxDir = goalBottomDir;	
   			}
   			
   			Vector opponentPlayer = new Vector();
   			Iterator i = players.iterator();
   		
   			while(i.hasNext()){
   				Player p = (Player) i.next();		
   				//Die mit dem anderen doofen Trikot und die, die wir sehen...	
   				if (Player.TEAM_OPPONENT == p.getTeam() && 
   					(p.getLastSeenTime() == seeTime) &&
   					(angleToObject(p.getAbsX(), p.getAbsY()) <= maxDir) &&
   					(angleToObject(p.getAbsX(), p.getAbsY()) >= minDir)){
   						opponentPlayer.addElement(new Double(angleToObject(p.getAbsX(), p.getAbsY())));
   				}
   			}
   			//... und die Torfosten
   			opponentPlayer.addElement(new Double(minDir));
   			opponentPlayer.addElement(new Double(maxDir));
   			// Das Array dann nach den Directions sortieren.
   			Collections.sort(opponentPlayer);
   			
   			
   			//Den maximalen Winkel berechnen.
   			double maxDeltaDir = -999; 	
   			double memADir=-999; 
   			double memBDir=-999;
   			boolean flag = true;
   			Iterator j = opponentPlayer.iterator();
   			int counter=0;
   				while (j.hasNext()) {
   					if (counter == 0) memADir = Double.parseDouble(j.next().toString());
   					if (j.hasNext()) memBDir = Double.parseDouble(j.next().toString());
   						if ((memBDir-memADir)  > maxDeltaDir) {
   							maxDeltaDir = (memBDir-memADir);
   						}
   					counter++;
   					memADir = memBDir;
   			}
   			return maxDeltaDir;
   		}   	    
   	    
	
	public void kickToGoal(){	
		//Zum Ball rennen.
		runToBall(100,20);
		if (playDirection == 1) {
			//Das Tor angucken, um alle relevanten Spieler zu erfassen	
			baseFunctions.turnAbs(angleToObject(getFlag("g r").getAbsX(), getFlag("g r").getAbsY()));
		} else{
			//Das Tor angucken, um alle relevanten Spieler zu erfassen
			baseFunctions.turnAbs(angleToObject(getFlag("g l").getAbsX(), getFlag("g l").getAbsY()));
			try {
				Thread.sleep(500);
			} catch (Exception e){
			}
		}
		//aufs Tor Schießen.
		baseFunctions.kick(100, calculateGoalKickDir() - getBodyDirection());
		setBallPossession(BALL_POSSESSION_OPPONENT);
	}
	
	
	public Vektor calculateGoaliePosition(){
	   	 Vektor goalBallVektor = new Vektor(ball.getAbsX() - getOurGoal().getAbsX(), ball.getAbsY() - getOurGoal().getAbsY());
	   	 Vektor groundLineVektor = new Vektor(0,1);
	   	 double alpha = Geometrie.angleBetweenVectors(goalBallVektor,groundLineVektor);
	   	 // die 8 ist die grundlinie vom Tor entfernt
	   	 double deltaX = ball.getAbsX() - (-52.5 + 2);
	   	 double deltaY = deltaX/Math.tan(alpha);
	   	// System.out.println("alpha"+ alpha);
	   	 //System.out.println("deltaX"+ deltaX);
	   	// System.out.println("deltaY"+ deltaY);
	   	 //System.out.println("Balll: "+ ball.getAbsX() + ":" + ball.getAbsY());
	   	 //Ränder abfangen
	   	 double y = ball.getAbsY()-deltaY;
	   	 double x = ball.getAbsX()-deltaX;
	   	 if ((Math.abs(y) > goalWidth/2) && (y > 0)) y = goalWidth/2;
	   	 if ((Math.abs(y) > goalWidth/2) && (y < 0)) y = -goalWidth/2;
	   	 return new Vektor(x,y);
	}
	
	
public void goalieGoToPosition(){
	if ((seeTime - ball.getLastSeenTime()) > 5 ){
	    return;
	}
    
	/*while(ball.getAbsX distTo( 50.5, 0))*/
    
    double destX = calculateGoaliePosition().getX();
	double destY = calculateGoaliePosition().getY();
	runToXY(destX, destY,100);
	if (ball.getAbsY()>=0 )baseFunctions.turnAbs(90);
	else baseFunctions.turnAbs(-90);
	baseFunctions.changeViewWithoutWaiting("narrow","high");
	while (checkOuterState() 
	        && (ball.getDist() > catchableAreaL) 
			&& (ballPossession != BALL_POSSESSION_ME)
			&& (seeTime - ball.getLastSeenTime() <= 5)
			&& (ball.getDist() < 20)){
		destX = calculateGoaliePosition().getX();
		destY = calculateGoaliePosition().getY();
		if (ball.getLastSeenTime() < seeTime) {
			baseFunctions.changeViewWithoutWaiting("normal","high");
			turnNeckToBall();
			baseFunctions.changeViewWithoutWaiting("narrow","high");
			if (ball.getAbsY()>=0 )baseFunctions.turnAbs(90);
			else baseFunctions.turnAbs(-90);
		}
		
		if (ball.getAbsY()>=0 && oldBall.getAbsY()<=0) {
			baseFunctions.turnNeckWithoutWaiting(Geometrie.normAngle(ball.getDir()+180));
			baseFunctions.turnAbs(90);
		}
		else if (ball.getAbsY()<=0 && oldBall.getAbsY()>=0){
			baseFunctions.turnNeckWithoutWaiting(Geometrie.normAngle(ball.getDir()-180));
			baseFunctions.turnAbs(-90);
			} else {
				baseFunctions.turnNeckWithoutWaiting(ball.getDir());
		
				double distToDestination = destY - yPlayer;
				
				if (Math.abs(angleToObject (destX, destY) - bodyDirection) < 90
						&& Math.abs(distToDestination) > 1){
					baseFunctions.dash(100);					       
				}
				else if (Math.abs(distToDestination) > 1){
					baseFunctions.dash(-100);					
				}
			}
			Thread.yield();
		}	
	if((ball.getDist() <= catchableAreaL) 
	        && (getBallPossession() != BALL_POSSESSION_ME)){ 
	    int oldCatchCount = catchCount;
		baseFunctions.catchBall(ball.getDir() + headDirection);
	    if (catchCount>oldCatchCount)setBallPossession(BALL_POSSESSION_ME);
	    
	}
}

public void goalieFreeKickAction(){
	baseFunctions.turnAbs(0);
	baseFunctions.changeViewWithoutWaiting("normal","high");
	try {
		double moveX=-52.5+2;
		double moveY=0;
		Flag  penaltyTop;
		Flag  penaltyBottom; 
		Player p = findFreePlayer();
		if ( p!= null){
			if (playDirection == -1) {
				penaltyTop = getFlag("p r t");
				penaltyBottom = getFlag("p r b");
			} else{
				penaltyTop = getFlag("p l t");
				penaltyBottom = getFlag("p l b");
			}
			
			if (p.getAbsX() > penaltyTop.getAbsX()-5){
				moveX = penaltyTop.getAbsX()-5;
			} else {
				moveX = p.getAbsX()-5;
			}
			
			if (p.getAbsY() <= penaltyTop.getAbsY()-5){
				moveY = penaltyTop.getAbsY()-5;
			}
			else if (p.getAbsY() > penaltyBottom.getAbsY()-5){
				moveY = penaltyBottom.getAbsY()-5;
			}
			else {
				if (p.getAbsY() <= 0) moveY = p.getAbsY() +5;
				if (p.getAbsY() > 0) moveY = p.getAbsY() -5;
			}
		
			baseFunctions.move(moveX,moveY);
			passToFreePlayer(p);
		} else {
			kickToGoal();
		}
	}catch (NoFreePlayerException e){
		kickToGoal();
	}		   	
}

    public void handlePassMessage(){
        //Nachricht holen und in m speichern        
        setBallPossession(BALL_POSSESSION_ME);
        if (passMessage == null) return;    	
     	double destinationX = passMessage.getX();
     	double destinationY = passMessage.getY();
     	double angleToDest = angleToObject(destinationX, destinationY); 
     	//Kopf in Richtung Spieler/Ball drehen:
     	baseFunctions.turnNeckRelToBodyWithoutWaiting(passMessage.getDirectionOfSender() 
     	        - Geometrie.normAngle(angleToDest - bodyDirection));
     	//Koerper in Richtung Ziel drehen:
     	baseFunctions.turn(Geometrie.normAngle(angleToDest - bodyDirection));
     	//und jetzt ab zur richtigen Position:
     	while (((Geometrie.distance(xPlayer, yPlayer, destinationX, destinationY) > 0.7)
     	        	|| (ball.getDist() > 30)) 
     	        && (ball.getDist() / ball.getSpeed() > 3)
				&& (ball.getDist() <= oldBall.getDist())
     	        && checkOuterState()
     	        && !kickTimeOut(GET_PASS_TIMEOUT)){
     		
     	    if (   (ball.getLastSeenTime() < seeTime)
   	    		&& (ball.getDist() <= oldBall.getDist())
     	        && !kickTimeOut(GET_PASS_TIMEOUT)){
	            ballPossession = BALL_POSSESSION_OPPONENT;
     	        passMessage = null;
     	        return;
     	    }
     	    if (ball.isSpeedKnown()){
     	        try{
     	            Vektor destination = Geometrie.lotVonPunktAufGerade(xPlayer, yPlayer, ball.getAbsX(), ball.getAbsY(), ball.getSpeedX(), ball.getSpeedY());
     	            destinationX = destination.getX();
     	            destinationY = destination.getY();
     	            angleToDest = angleToObject(destinationX, destinationY);
     	        } catch (NullVektorException e){
     	        	baseFunctions.turnNeckRelToBodyWithoutWaiting(0);
     	            ballPossession = BALL_POSSESSION_OPPONENT;
     	        	passMessage = null;
     	        	return;
     	    	}
     	    }
     	    if (Math.abs(angleToDest - bodyDirection)> 20){ 
     	        baseFunctions.turnNeckWithoutWaiting(Geometrie.normAngle(ball.getDir() - angleToDest + bodyDirection));     	         	        
     	        baseFunctions.turnAbs(angleToDest);
     	    } else {
     	        int power = 100;
     	        baseFunctions.turnNeckWithoutWaiting(ball.getDir());
     	      /*  if (distTo(destinationX, destinationY) < 1.5) {
     	            power = (int) (distTo(destinationX,destinationY)*30);   					
     	        }*/
     	       baseFunctions.dash(power);
     	    }
     	}
     	if (!kickTimeOut(GET_PASS_TIMEOUT)){
     		stopBall();     		
     	}
     	passMessage = null;
     }
    
    public void stopBallWithoutNonsense(){
        runToBall(100, DRIBBLE_TIMEOUT);
        try{ 
    	    baseFunctions.kick(30 * (-ball.getDistChange()
    		        / (1 - 0.25 * (Math.abs(ball.getDir()/180))- 0.25 * (ball.getDist()/kickableMarginDeltaMax))
    		                ), 0);    	        	    
    	}catch (ObjectInvisibleException e){	        
	        int direction = 1;
	        if( 
	            new Vektor(ball.getSpeedX(), ball.getSpeedY())
	            .mul(new Vektor(1, 0).turn(bodyDirection)) > 0
	        ) direction = -1;    	    
	        
    		baseFunctions.kick(30 * (direction * ball.getSpeed()
    		        / (1 - 0.25 * (Math.abs(ball.getDir()/180))- 0.25 * (ball.getDist()/kickableMarginDeltaMax))
    		                ), 0);        		
    	}
    }
    
    public void stopBall(){        
        if(ball.getLastSeenTime() < seeTime - 5){
	        ballPossession = BALL_POSSESSION_OPPONENT;
            return;
        }
        //searchBall();
        baseFunctions.turnNeckRelToBodyWithoutWaiting(0);
        baseFunctions.turnCorrect(ball.getDir() + headDirection);        
        baseFunctions.changeViewWithoutWaiting("narrow", "high");
        /*System.out.println("nach der korrekten Drehung in StopBall" + (senseBodyTime - startTime));*/
        while((getBall().getLastSeenTime() > getSeeTime() - 2) 
                && (getBall().getDist() > getKickableMarginDeltaMax())
                //&& (new Vektor(ball.getAbsX() - xPlayer, ball.getAbsY() - yPlayer).mul(new Vektor(ball.getSpeedX(), ball.getSpeedY())) < 0)
                && checkOuterState()
                && !kickTimeOut(GET_PASS_TIMEOUT)
				&& checkPositionInActingField(xPlayer,yPlayer)){
        		Thread.yield();	
        	}
        if(kickTimeOut(GET_PASS_TIMEOUT)) {
            baseFunctions.changeViewWithoutWaiting("normal", "high");
            return;
        }        
    	try{ 
    	    baseFunctions.kick(30 * (-ball.getDistChange()
    		        / (1 - 0.25 * (Math.abs(ball.getDir()/180))- 0.25 * (ball.getDist()/kickableMarginDeltaMax))
    		                ), 0);

    	    
    	    
    	}catch (ObjectInvisibleException e){	        
	        int direction = 1;
	        /*if( 
	            new Vektor(ball.getSpeedX(), ball.getSpeedY())
	            .mul(
	                    new Vektor(1, 0)
	                    .turn(bodyDirection)
	            ) > 0
	        ) direction = -1;*/    	    
	        
    		baseFunctions.kick(30 * (direction * ball.getSpeed()
    		        / (1 - 0.25 * (Math.abs(ball.getDir()/180))- 0.25 * (ball.getDist()/kickableMarginDeltaMax))
    		                ), 0);        		

    	}
    	//Checken, ob der Ball getroffen wurde:
    	kickTimeOut(DIRECT_TIMEOUT);
        baseFunctions.changeViewWithoutWaiting("normal", "high");        	
    }
    
    
    /*************************************************************************
     * Checkt, ob der Ball innerhalb der uebergebenen 
     * Zeit geckickt wurde. Macht einen Broadcast von LostBallMessage,
     * falls der letzte Ballkontakt zu lange her ist.
     * Wenn der Ball zur Aufrufzeit schon weg war, wird nur true
     * zurueckgegeben 
     * @param maxTime Die Zeit, vor der maximal geckickt worden
     * sein soll
     * @return false, falls OK, true, falls zu lange her oder Ball schon weg
     *************************************************************************
     */
    public boolean kickTimeOut(int maxTime){
        if (ballPossession == BALL_POSSESSION_OPPONENT) return true;
        if (senseBodyTime - lastKickTime  > maxTime) {
            ballPossession = BALL_POSSESSION_OPPONENT;
            return true;
        }
        else return false;
    }
    
   
   public void interceptBall(){
    int kick = kickCount;
    baseFunctions.changeView("narrow", "high");
    while (kick >= kickCount 
   	        && checkOuterState()
   	        && (ballPossession == BALL_POSSESSION_OPPONENT)
    		&& (checkBallInActingField())
    		&& ((personToCover == -1) || (ball.getDist() < 10))){
   	    searchBall(1);
		int pow = 100;
		
		while (checkOuterState() 
		       && (ball.getDist() > kickableMarginDeltaMax)
		       && (ballPossession == BALL_POSSESSION_OPPONENT)
			   && (checkBallInActingField()
			   && ((personToCover == -1) || (ball.getDist() < 10)))){
			//Falls wir immer ueber den Ball wegrennen, wieder rein:
		    /*if (distTo(x,y) < 1) {
				pow = (int) (distTo(x,y)*10);
			}
			else*/ pow = 100;	
			double alpha = ball.getDir()+headDirection+bodyDirection;
			dashTo(alpha, pow);
			while(!searchBall(1) && checkOuterState){};
		}
		
		baseFunctions.changeView("normal", "high");
		
		Iterator i = players.iterator();
		double minDistToPlayer=Double.POSITIVE_INFINITY;
		Player minDistPlayer = null;
		while(i.hasNext()){
			Player p = (Player) i.next();		
			//Die gegner
			if 	(Player.TEAM_OPPONENT == p.getTeam() && 
				(p.getLastSeenTime() +2 > seeTime) &&
				(p.getDist() < 10)&&
				(p.getDist() < minDistToPlayer)){
				minDistPlayer = p;
				minDistToPlayer = p.getDist();
			}
		}
		if((ballPossession == BALL_POSSESSION_WE)
		    ||!checkOuterState()
			|| !checkBallInActingField()
			||((personToCover != -1) && (ball.getDist() >= 10))){
			return;
		}
		
		if (minDistPlayer != null){	    
		    setBallPossession(BALL_POSSESSION_ME);
		   // if (ball.getDist() < kickableMarginDeltaMax) stopBallWithoutNonsense();	
		    try{	
		    	passToFreePlayer(getFreePlayer());
		    } catch (NoFreePlayerException e){
		    	Vektor opponentBallVektor = new Vektor(ball.getAbsX() - minDistPlayer.getAbsX(), ball.getAbsY() - minDistPlayer.getAbsY()) ;
				double kickDir = (Math.asin(opponentBallVektor.getY()/opponentBallVektor.getX()) / (Math.PI * 2) * 360) + 135;
				baseFunctions.kick(50, kickDir - getBodyDirection());
				baseFunctions.turnAbs(kickDir);
				setBallPossession(BALL_POSSESSION_OPPONENT);
			}
		} else {
		    setBallPossession(BALL_POSSESSION_ME);
		}
   	}	
}
  
   
   
    

    /*************************************
     * Zählt die Gegenspieler, die in einem gegebenen Winkel 
     * und einer gegebenen Distanz vor uns stehen
     * @param dir abweichung von unserer ViewDirection
     * @param dist maximale Distanz zum Objekt
     * @return int Anzahl der Gegenspieler
     ********************************************/
        
    public int countOpponentsInCone(int dist, int dir){
    	int covered=0;
    	Iterator j = players.iterator();
    	while (j.hasNext()){
    		Player o = (Player) j.next();
    		if ((o.getTeam() != Player.TEAM_WE) 
    			&& (o.getDist() < dist) 
    			&& (Math.abs(o.getDir()) < dir)
    			&& o.getLastSeenTime() == seeTime){    			
    			covered++;
    		}
    	}			
    	return covered;
    }
    
	/*******************************************************
	 * Dreht seinen Kopf zum Ball
	 * Benoetigt evtl. mehrere Runden, um den Ball zu finden
	 * und evtl. eine, um einen turn-befehl zu verschicken
	 *******************************************************/
	//TODO: in Bearbeitung
	public void turnNeckToBall(){
			int change = 1;
		int counter=0;
		while ((ball == null) || (getBall().getLastSeenTime() < seeTime)) {
			if (counter == 2){
				baseFunctions.turn(180);
				counter=0;
			}
			baseFunctions.turnNeckRelToBody(change * 45);
			change = change * -1;
			counter++;
		}
	    baseFunctions.turnNeck(ball.getDir());
	}


    /**
     * 
     */
    public void resetLastKickTime() {
        lastKickTime = senseBodyTime;        
    }

    
    public void coordinateDefense(){
    	// erst die Lage peilen
    	baseFunctions.turnNeckRelToBodyWithoutWaiting(0);
    	baseFunctions.changeViewWithoutWaiting("wide","high");
    	baseFunctions.turnAbs(0);
    	
    	// dann die gegner raussuchen
    	Vector opponentPlayer = new Vector();
    	
		Iterator i = players.iterator();
		while(i.hasNext()){
			Player p = (Player) i.next();		
			//Die mit dem anderen doofen Trikot und die, die wir sehen...	
			if ((Player.TEAM_OPPONENT == p.getTeam()) 
					&& (p.getLastSeenTime() + 1 >= seeTime)
					&& p.isSeen()){
				opponentPlayer.addElement(p);
			}		
		}

		//Die Verteidigung identifizieren
		Vector friendlyPlayer = new Vector();
		Player defenseLeft = getPlayer(DEFENSE_LEFT, Player.TEAM_WE);
		Player defenseCenter = getPlayer(DEFENSE_CENTER, Player.TEAM_WE);
		Player defenseRight = getPlayer(DEFENSE_RIGHT, Player.TEAM_WE);
		if (defenseLeft != null) friendlyPlayer.addElement(defenseLeft);
		if (defenseCenter != null) friendlyPlayer.addElement(defenseCenter);
		if (defenseRight != null) friendlyPlayer.addElement(defenseRight);
		
		
		
		//Die "drei" (anzahl der Verteidiger) , die am nächsten zum Tor stehen
		Vector opponentPlayerMinDistToGoal = new Vector();
		double minDistToGoal= Double.POSITIVE_INFINITY;
		Player elemToMove = null; 
		
		Iterator oPm = opponentPlayer.iterator();
		int friendlyPlayerSize = friendlyPlayer.size();
		while ((opponentPlayerMinDistToGoal.size() < Math.min(friendlyPlayerSize, 2)) 
		        && oPm.hasNext()){
			while (oPm.hasNext()){
				Player p = (Player) oPm.next();
				if (p.getDist() < minDistToGoal) elemToMove = p;
			}
			opponentPlayer.removeElement(elemToMove);
			opponentPlayerMinDistToGoal.add(elemToMove);
			oPm = opponentPlayer.iterator();
		}
					
		//Die Zuordnung, wer zu wem
		Iterator j = opponentPlayerMinDistToGoal.iterator();	
		Iterator k = friendlyPlayer.iterator();
		
		double minDist= Integer.MAX_VALUE;
		Player elemFP = null;
		Player elemOP = null;
		int coverCount = 0;
		int [][] coverPairs = 
		    {{0, 0}, {0, 0}};
		while (k.hasNext() && j.hasNext() && checkOuterState()){
			minDist= Integer.MAX_VALUE;
			while(j.hasNext()){
				Player oP = (Player) j.next();
				while (k.hasNext()){
					Player fP = (Player) k.next();
					double checkDistfPoP = Geometrie.distance(oP.getAbsX(), oP.getAbsY(),fP.getAbsX(), fP.getAbsY());
					if (checkDistfPoP < minDist){
						minDist = checkDistfPoP;
						elemFP = fP;
						elemOP = oP;
					}	
				}
				k = friendlyPlayer.iterator();
			}
			opponentPlayerMinDistToGoal.removeElement(elemOP);
			friendlyPlayer.removeElement(elemFP);
			j = opponentPlayerMinDistToGoal.iterator();
			k = friendlyPlayer.iterator();
			try{
				coverPairs[coverCount][0] = elemFP.getUnum();
				coverPairs[coverCount][1] = elemOP.getUnum();
			    
			} catch (ObjectInvisibleException e){
				/*doNothing*/
			}			
			coverCount++;
		}
		baseFunctions.changeViewWithoutWaiting("narrow","high");
		while(senseBodyTime % 2 == 0){		    
		    double dir = Geometrie.normAngle(angleToObject(-50.5, 0) - bodyDirection);		
			if ((distTo(-50.5, 0) > 1)
			        && Math.abs(dir) > 20){
			    baseFunctions.turn(dir);
			} else Thread.yield();
		}
		baseFunctions.sayCover(
		        coverPairs[0][0],
		        coverPairs[0][1],
		        coverPairs[1][0],
		        coverPairs[1][1]);
		if(distTo(-50.5, 0) > 1) dashTo(angleToObject(-50.5, 0), 100);
		baseFunctions.changeViewWithoutWaiting("normal","high");
    }
    
    public boolean checkLastingPlayMode(){
    	switch (playMode){
    		case PLAY_ON:
    			return true;
    		case TIME_OVER:
    			return true;
    		case KICK_OFF_WE:
    			return true;
    		case KICK_OFF_OPPONENT:
    			return true;
    		case FREE_KICK_WE:
    			return true;
    		case FREE_KICK_OPPONENT:
    			return true;
    		case GOAL_KICK_WE:
    			return true;
    		case GOAL_KICK_OPPONENT:
    			return true;
    		
    		default:
    		    return false;
    	}
    }
    
}
