/*
 * Created on 06.12.2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package knowledgeAndTechnics;

import utilities.Geometrie;

import communication.MessageParser;
import communication.Sender;

/**
 * @author Administrator
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class BaseFunctions {

    public static final int MSG_BROADCAST = 88;
    
    private Intelligence infoPool;
    private Sender sender;
    private MessageParser messageParser;
    
	private int lastSayTime = -1;

    public BaseFunctions(Sender sender, Intelligence infoPool) {
        this.sender = sender;
        this.infoPool = infoPool;
    }

    public void setMessageParser(MessageParser messageParser){
    	this.messageParser = messageParser;
    }
    
	public void init(String team){
		sender.init(team);
		try{	 
			Thread.sleep(1000);
		}catch (Exception e){/*doNothing*/}
	}
	
	public void init(String team, boolean goalie){
		sender.init(team, goalie);
		try{	 
			Thread.sleep(1000);
		}catch (Exception e){/*doNothing*/}
	}
	
	public void reconnect(String team, int unum){
		sender.reconnect(team, unum);
	}
	
	public void bye(){
		sender.bye();
	}
	
	/*move, say, etc...*/
	public void catchBall(double moment){	
		sender.catchBall(moment);
		waitAfterSending();
	}
	
	
	public void changeView(String width, String quality){
	    sender.change_view(width, quality);
	    waitAfterSending();
	}
	
	public void changeViewWithoutWaiting(String width, String quality){
	    sender.change_view(width, quality);
	}
	
	public void dash(int power){
	    //System.out.println(infoPool.getSenseBodyTime() + " hab mich bewegt mit " + power);
	    sender.dash(power);
		waitAfterSending();
	}
	
	public void kick(double power, double direction){
		sender.kick(power, direction);
		waitAfterSending();
	}
 
	public void move(double x,double y){	
		sender.move(x, y);
		waitAfterSending();
	}
	// es sind bis zu 10 Zeichen erlaubt zu sagen
	public void sayTo(int receiverUnum, String message){	    
        message = convertUnumToString(receiverUnum) + message;
        //Hat Deadlock erzeugt, Vorsicht mit gleichzeitigen Nachrichten!
        /*while(lastSayTime == infoPool.getSenseBodyTime())*/ Thread.yield();	    
        if (message.length() > 10) System.err.println("Sender Say: Zeichenkette zu lang");
        sender.say(message);
        lastSayTime = infoPool.getSenseBodyTime();	    
	}
	
	/**
	 * Gibt die Anweisung an einen Spieler, zu einer Position zu laufen um den 
	 * Ball anzunehmen 
	 * @param receiverUnum Trikotnummer des Spielers, der den Ball erhalten soll
	 * @param x X-Koordinate für die zukünftige Position des Balls
	 * @param y Y-Koordinate für die zukünftige Position des Balls
	 * @param passDirection Die absolute Richtung, in die de Pass gespielt wird. 
	 * Wird in 45°-Schritten codiert. Codierung: <br>
	 * <table>
	 * <tr><th>Bereich</th><th>wird codiert als</th></tr>
	 * <tr><td>157,5 bis 180°, -157,5 bis -180 °</td> <td>0</td></tr>
	 * <tr><td>-112,5 bis - 157,5°</td>               <td>1</td></tr>
	 * <tr><td>-67,5 bis -112,5°</td>                 <td>2</td></tr>
	 * <tr><td>-22,5 bis -67,5</td>                   <td>3</td></tr>
	 * <tr><td>-22,5 bis 22,5</td>                    <td>4</td></tr>
	 * <tr><td>22,5 bis 67,5</td>                     <td>5</td></tr>
	 * <tr><td>67,5 bis 112,5°</td>                   <td>6</td></tr>
	 * <tr><td>112,5 bis 157,5°</td>                  <td>7</td></tr>
	 * </table>
	 */
	public void sayPassTo(int receiverUnum, int x, int y, int passDirection){
	    if (passDirection >= 180) passDirection = passDirection - 360;
		if (passDirection < -180) passDirection = passDirection + 360;
	    int passSegment = (int)(Math.round((double)passDirection / 45) + 4) % 8; 
		String message = "p"+ convertPositionToString(x) + convertPositionToString(y) + passSegment;
		//System.out.println("Sender, sayPassTo:" +message);
		sayTo(receiverUnum, message);
	}
	
	public void sayCover(int friend1, int enemy1, int friend2, int enemy2){
	    String message = "c"	        
	        + Integer.toHexString(friend1) + Integer.toHexString(enemy1)
	        + Integer.toHexString(friend2) + Integer.toHexString(enemy2);
	    sayTo(88, message);
	}
		
	public void broadcastBallPosessionWe(){
		int myUnum = infoPool.getUnum();	    
		String message = "w";
	    //System.out.println("Sender, broadcastBallPossession:" + message);
	    sayTo(MSG_BROADCAST, message);
	}
	
	
	/**
	 * Konvertiert eine ganzzahligePositionsangabe in einen 
	 * dreistelligen String. Dient der Kodierung für die Say nachrichten.
	 * z.B. für "56" --> "056" oder x "-1" --> "-01"
	 * @param i
	 * @return Der nachrichtenkonforme String
	 */
	private String convertPositionToString(int i){
		if ((i < -99)||(i > 99)) return null;
		String s = Integer.toString(i);
		if(i >= 0){
			s = "0" + s;
			if (s.length() == 2) s = "0" + s;
		} else {
			if (s.length() == 2) s = "-" + "0" + s.charAt(1);
		}
		return s;
	}
	
	/**
	 * Konvertiert eine ganzzahlige Trikotnummer in einen 
	 * zweistelligen String. Dient der Kodierung für die Say nachrichten.
	 * z.B. für "1" --> "01" oder "11" --> "11"
	 * @param i
	 * @return Der nachrichtenkonforme String
	 */
	private String convertUnumToString(int i){
		if ((i < 0)||(i > 99)) return null;
		String s = Integer.toString(i);
		if (s.length() == 1) s = "0" + s;
		return s;
	}
	
	public void senseBody(){
		sender.senseBody();	
	}
	
	public void score(){
		sender.score();	
	}

/*************************************************************
 * Fuehrt eine korrekte Drehung aus. Benoetigt ggf. doppelt so lange
 * wie turn(actualAngle)
 * @param actualAngle Der Winkel, um den gedreht werden soll 
 **************************************************************/
	public void turnCorrect(double actualAngle){ 
	   // System.out.println(infoPool.getSenseBodyTime() + " hab mich gedreht mit " + actualAngle);
	    actualAngle = Geometrie.normAngle(actualAngle);
	    double absDestinationAngle = Geometrie.normAngle(infoPool.getBodyDirection() + actualAngle);
	    double moment = actualAngle * 
		        (1 + Intelligence.INERTIA_MOMENT * infoPool.getAmountOfSpeed());
	    if (moment >= 180){
	        moment = 180;
	        sender.turn(moment);
	        waitAfterSending();
	        int missedAngle = (int)Math.round(Geometrie.normAngle(absDestinationAngle - infoPool.getBodyDirection()));
	        this.turn(missedAngle);
	    }
		else if (moment < -180){
		    moment = -180;
			sender.turn(moment);
		    waitAfterSending();
		    int missedAngle = (int)Math.round(Geometrie.normAngle(absDestinationAngle - infoPool.getBodyDirection()));
		    this.turn(missedAngle);
		} 
		else {
		    sender.turn(moment);
		}
		waitAfterSending();
		
	}
/**
 * Fuehrt eine Drehung um einen uebergebenen Winkel aus. Versucht, eine 
 * eventuelle Abweichung, die durch die Spielergeschwindigkeit verursacht
 * wird, auszugleichen. Falls der Betrag des dazu benoetigte Moments 
 * groesser als 180° ist, ist die Drehung "falsch". Fuer eine genauere
 * Drehung sollte turnCorrect(double actualAngle) verwendet werden.
 * @param actualAngle Der Winkel, um den gedreht werden soll
 */
	public void turn(double actualAngle){ 
	    double moment = Geometrie.normAngle(actualAngle) * 
		        (1 + Intelligence.INERTIA_MOMENT * infoPool.getAmountOfSpeed());
		if (moment > 180) moment = 180;
	    if (moment < -180) moment = -180;
		sender.turn(moment);
		waitAfterSending();
	}
	
	private double normTurnNeckDegrees(double degrees){
	    double minTurn = -90 - infoPool.getHeadDirection();
	    double maxTurn =  90 - infoPool.getHeadDirection();
	    while(degrees < minTurn - 90) degrees += 360;
	    while(degrees > maxTurn + 90) degrees -= 360;
	    if (degrees < minTurn) degrees = minTurn;
	    if (degrees > maxTurn) degrees = maxTurn;
	    return degrees;
	}
	
	/************************************************************
	 * Dreht den Kopf um die angegebene Gradzahl (-180 bis +180)
	 * @param degrees Der Winkel, um den der Kopf gedreht wird
	 **********************************************************/
	public void turnNeck(double degrees){		
		sender.turnNeck(normTurnNeckDegrees(degrees));
		waitAfterSending();
	}
	
	/************************************************************
	 * Dreht den Kopf um die angegebene Gradzahl (-180 bis +180)
	 * Wartet nicht auf die naechste Sicht.
	 * @param degrees Der Winkel, um den der Kopf gedreht wird
	 **********************************************************/
	public void turnNeckWithoutWaiting(double degrees){    
	    sender.turnNeck(normTurnNeckDegrees(degrees));
	}
	
	/*****************************************************************
	 * Dreht den Kopf auf eine Position relativ zum Koerper
	 * wartet nicht auf die naechste Sicht.
	 * param degrees -90° bis 90°: Die gewuenschte Richtung des Kopfes 
	 ****************************************************************/
	public void turnNeckRelToBodyWithoutWaiting(double degrees){
	    turnNeckWithoutWaiting(degrees - infoPool.getHeadDirection());
	}
	

	
	public void waitAfterSending(){
		int senseBodyTime = infoPool.getSenseBodyTime();
		while (infoPool.checkLastingPlayMode()&&(infoPool.getSenseBodyTime() <= senseBodyTime)) 
			Thread.yield();
		while (infoPool.checkLastingPlayMode()&&(infoPool.getSeeTime() <= senseBodyTime)) 
			Thread.yield();
	}	
	
	public void turnAbs(double angle){
		turnCorrect(angle -  infoPool.getBodyDirection());	    
	}
	
    public void turnNeckRelToBody(double degrees){
	    turnNeck(degrees - infoPool.getHeadDirection());
	}
    
}
