<html>
<xmp>
/**
 * Title: iSIM
 * Copyright: Copyright (c) 2002
 * Company:   University of Oregon Computer Science Dept.
 *
 * @author Jason Prideaux
 * @version 1.0
 *
 */

package agent.autonomous;

import agent.*;
import controller.*;
import gui.*;
import transport.*;
import transport.protocol.*;

import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import java.io.*;


/* ====================================================================== */
/** This class represents a mobile autonomous robot.  It has knowledge and
 *  state.  The states determine the current goal of the robot.  This robot
 *  simply waits at a given spot until a searcher notifies it that a person
 *  in distress has been found.  The Rescuer will go to the person and send
 *  a message to the person to notify the person that he/she has been saved.
 *
 *  When the robot senses data/communication it will respond by making some
 *  sort of actuation.  So if the sensors were to fail.  This robot would
 *  be dead in the water.
 *
 */
public class Rescuer extends Autonomous implements SocketReceiverListener{

	/** The simulation controller, only used to determine our simulation
	    environment boundaries. */
	private Controller controller;

	/** A thread used for this robot to send wireless communication message. */
	private Calling calling;

  // Knowledge
 	/** The location of person that needs to be rescued. */
	private Point distress;

	/** Our starting/home position. */
	private Point dispatch;

  // States
	/** If true then remain idle. */
	private boolean idle;

	/** If true then go rescue person. */
	private boolean rescuing;

	/** Ir true then return to starting/home position. */
	private boolean returning;


	/* ====================================================================== */
	/** This constructor sets up the information needed for the robot.
	 *  Naturally the robot must know its own abilities.  The simulation
	 *  controller is needed so that it can be given to the mediator class,
	 *  Autonomous.java, which serves as an inbetween for a robot and the
	 *  simulator.
	 *
	 *  @param  c   The iSIM controller.
	 *  @param  loc  The location of the agent.
	 *  @param  port  The communication port need to speak with iSIM.
	 *  @param  range   The wireless max range.
	 *
	 */
	public Rescuer(Controller c, Point loc, int port, int range){

		// Super our agent/user information.
		super(c, loc, port, range, "rescuer");

		// Simulation controller
		//controller = c;
		dispatch = loc;

		idle = true;
		rescuing = false;
		returning = false;

		calling = new Calling();
		calling.start();


	} //Constructor


	// Use this method to receive new location data.
	public void senseLocation(Point location, double heading){

		if( !idle ){

			Point newLocation = location;
			double newHeading = heading;

			if( rescuing ){

				// Determine angle to distress to get help from
				double hyp = Controller.diff(distress, location);
				double opp = Math.abs(location.getY() - distress.getY());

				double angle = Math.toDegrees(Math.asin(opp/hyp));
				double fixed = 0;

				if( location.getX() <= distress.getX() && location.getY() <= distress.getY() ){
					fixed = 360 - angle;

				}else if( location.getX() > distress.getX() && location.getY() <= distress.getY() ){
					fixed = 180 + angle;

				}else if( location.getX() <= distress.getX() && location.getY() > distress.getY()){
					fixed = angle;

				}else if( location.getX() > distress.getX() && location.getY() > distress.getY()){
					fixed = 180 - angle;
				}

				double adjust = fixed%45;

				if( adjust < 22.5 ){
					newHeading = ((int)(fixed/45))*45;
				}else{
					newHeading = ((int)(fixed/45)+1)*45;
				}

				if( newHeading < 0 ){
					newHeading = 315;
				}else if( newHeading > 315 ){
					newHeading = 0;
				}


				int x = (int)(location.getX()+(Math.cos(Math.toRadians(newHeading))*15));
				int y = (int)(location.getY()+(Math.sin(Math.toRadians(newHeading))*-15));

				if( x < 15 ){
					newHeading = 0;
				}else if( y < 15 ){
					newHeading = 270;
				}else if( x > (controller.getChartComp().getSize().getWidth()-15) ){
					newHeading = 180;
				}else if( y > (controller.getChartComp().getSize().getHeight()-15) ){
					newHeading = 90;
				}else{
					newLocation = new Point(x,y);
				}

				actuateLocation( newLocation, newHeading);


			}else if( returning ){

				// Determine angle to dispatch to get help from
				double hyp = Controller.diff(dispatch, location);
				double opp = Math.abs(location.getY() - dispatch.getY());

				double angle = Math.toDegrees(Math.asin(opp/hyp));
				double fixed = 0;

				if( location.getX() <= dispatch.getX() && location.getY() <= dispatch.getY() ){
					fixed = 360 - angle;

				}else if( location.getX() > dispatch.getX() && location.getY() <= dispatch.getY() ){
					fixed = 180 + angle;

				}else if( location.getX() <= dispatch.getX() && location.getY() > dispatch.getY()){
					fixed = angle;

				}else if( location.getX() > dispatch.getX() && location.getY() > dispatch.getY()){
					fixed = 180 - angle;
				}

				double adjust = fixed%45;

				if( adjust < 22.5 ){
					newHeading = ((int)(fixed/45))*45;
				}else{
					newHeading = ((int)(fixed/45)+1)*45;
				}

				if( newHeading < 0 ){
					newHeading = 315;
				}else if( newHeading > 315 ){
					newHeading = 0;
				}


				int x = (int)(location.getX()+(Math.cos(Math.toRadians(newHeading))*15));
				int y = (int)(location.getY()+(Math.sin(Math.toRadians(newHeading))*-15));

				if( x < 15 ){
					newHeading = 0;
				}else if( y < 15 ){
					newHeading = 270;
				}else if( x > (controller.getChartComp().getSize().getWidth()-15) ){
					newHeading = 180;
				}else if( y > (controller.getChartComp().getSize().getHeight()-15) ){
					newHeading = 90;
				}else{
					newLocation = new Point(x,y);
				}

				if ( hyp > 15 ){
					actuateLocation( newLocation, newHeading);
				}else{
					returning = false;
					idle = true;
				}
			}
		}

	}



	// Use this method to receive communication from other robots
	// and people agents.
	public void senseMessage(HelpMessage msg){

		// If idle then we only listen to searcher to tell us to search again.
		if( idle ){

			if( ((HelpMessage)msg).getMessage().equalsIgnoreCase("searching") ){

				dispatch = this.getLocation();

			}else if( ((HelpMessage)msg).getMessage().equalsIgnoreCase("request") ){

				distress = ((HelpMessage)msg).getLocation();
				idle = false;
				rescuing = true;
				actuateMessage(new HelpMessage("idle", getPort(), 1, null ));
				actuateLocation( this.getLocation(), this.getPosition().getHeading());
			}

		// If searching then we only listen to a person in distress
		}else if( rescuing ){

			if( ((HelpMessage)msg).getMessage().equalsIgnoreCase("help") ){

				actuateMessage(new HelpMessage("rescuer", getPort(), 1, null ));
				rescuing = false;
				returning = true;

			}

		}
	}





	// Returns the current location of this autonomous robot.
	public Point getPoint(){

		return this.getLocation();
	}


	// Use this thread to communicate to other robots.
	class Calling extends Thread{

		public void run(){

			while(true){

				try{ Thread.sleep(2000); }catch(Exception e){}

				if( idle ){
					actuateMessage(new HelpMessage("rescuer", getPort(), 1, getPoint() ));
				}

			}

		}

	}



} //class: Rescuer
</xmp>
</html>
