/*
 * SmartWanderAction.java
 *
 * Created on April 7, 2004, 4:42 PM
 */

package PER.rover;

import PER.rover.control.RoverState;

/**
 * Allows the rover to explore an area without running in to things. The rover will
 * drive in a straight line until it encounters an obstacle. Then it will turn
 * in place by a random angle and drive in a new direction.
 *
 *
 * @author Emily Hamner
 */

public class SmartWanderAction implements Action {
    /** The length of time to wander in seconds. */
    private int secs;
    
    /** The time this action takes to complete in milliseconds. */
    private int time;
    
    private DriveToAction driveAct;
    private TurnToAction turnAct;
    private java.util.Random rand;
    private boolean takePics;
    
    transient private Rover rov = null;
    transient private Thread myThread = null;
    transient private boolean success;
    transient private int ret;
    transient private boolean quit = false;
    transient private long starttime;
    
    /** Creates a new instance of SmartWanderAction that takes pictures
     * as it moves. Use Rover.receive.getRecentImage() to get the pictures
     * as the robot is driving.
     *
     * @param seconds the length of time to wander in seconds
     */
    public SmartWanderAction(int seconds) {
        this(seconds, true);
    }
    
    /** Creates a new instance of SmartWanderAction. If the rover is taking pictures,
     * use Rover.receive.getRecentImage() to get the pictures as the robot
     * is driving.
     *
     * @param seconds the length of time to wander in seconds
     * @param takePictures if true, the rover will take pictures while
     * turning and driving
     */
    public SmartWanderAction(int seconds, boolean takePictures) {
        this.secs = seconds;
        time = secs * 1000;
        takePics = takePictures;
        driveAct = new DriveToAction(Integer.MAX_VALUE,0,DriveToAction.CYCLE_SAFETY,takePics);
        turnAct = new TurnToAction(0,takePics);
        rand = new java.util.Random();
    }
    
    public boolean doAction(Rover r) {
        //PER.rover.StatsLog.println(PER.rover.StatsLog.DRIVE,dist);
        rov = r;
        myThread = new Thread() {
            public void run() {
                starttime = System.currentTimeMillis();
                
                quit = false;
                success = wander();
                
                long endtime = System.currentTimeMillis();
                if (success)
                    time = (int)((endtime - starttime) );
            }
        };
        
        myThread.start();
        for(int i=0; i<10 && !myThread.isAlive(); i++)
            Thread.yield();
        return true;
    }
    
    public int getReturnValue() {
        return ret;
    }
    
    public String getShortSummary() {
        return "Smart Wander.";
    }
    
    public String getSummary() {
        return "Smart wander for "+secs+" seconds.";
    }
    
    public int getTime() {
        return time;
    }
    
    public int getTimeRemaining() {
        return time - (int)((System.currentTimeMillis() - starttime) );
    }
    
    public boolean isCompleted() {
        return myThread != null && !myThread.isAlive();
    }
    
    /** Returns true if the time limit expired without the occurance
     * of any errors.
     */
    public boolean isSuccess() {
        return success;
    }
    
    public void kill() {
        quit = true;
    }
    
    private boolean wander(){
        int ang = 0;
        double d = 0;
        
        while(getTimeRemaining() > 0){
            
            //drive until an obstacle
            driveAct.doAction(rov);
            while(!driveAct.isCompleted()){
                if(quit){
                    driveAct.kill();
                    ret = RoverState.KILLED;
                    return false;
                }
                try{Thread.sleep(20);}catch(Exception e) {}
            }
            if(driveAct.isSuccess()
            || driveAct.getReturnValue() == RoverState.OBSTACLE_DETECTED){
                //turn randomly
                //d = rand.nextGaussian(); //bias towards larger turns
                //random angle between 10 and 180 (or -180 and -10)
                d = rand.nextDouble();
                ang = (int)(d * 170) + 10;
                if(rand.nextBoolean())
                    ang *= -1;
                
                turnAct.setAngle(ang);
                
                turnAct.doAction(rov);
                while(!turnAct.isCompleted()){
                    if(quit){
                        turnAct.kill();
                        ret = RoverState.KILLED;
                        return false;
                    }
                    try{Thread.sleep(20);}catch(Exception e) {}
                }
                if(!turnAct.isSuccess()){
                    //error occurred during turn
                    ret = turnAct.getReturnValue();
                    return false;
                }
                
                //check for obstacles seen during the turn and move away?
                
            }else{
                //error occurred during drive
                ret = driveAct.getReturnValue();
                return false;
            }
        }
        
        ret = RoverState.SUCCESS;
        return true;
    }
    
    public long getImageUpdateTime() {
        if(rov != null)
            return rov.receive.getImageUpdateTime();
        else return 0;
    }
    
    public java.awt.image.BufferedImage getRecentImage() {
        if (rov != null)
            return rov.receive.getRecentImage();
        else return null;
    }
    
}
