import lejos.navigation.Pilot;
import lejos.navigation.TachoNavigator;
import lejos.nxt.Button;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.SensorPortListener;
import lejos.nxt.TouchSensor;
import lejos.subsumption.Arbitrator;
import lejos.subsumption.Behavior;

/**
 * Team Hadaga's COMP 2910 Spring 2008 program for the Power Missions table.
 * 
 * We chose to implement some behaviours at the Power table.
 *
 * @version 2.0
 * @author Team Hadaga
 */
public class PowerMissions {

    /**
     * Controls distance travelled.
     */    
    private static final int WHEEL_DIAMETER = 56;
    
    /**
     * Distance from centre of left wheel to centre of right wheel.  Controls
     * turn radius and centre point.
     */    
    private static final int TRACK_WIDTH = 135;
    
    /**
     * Used by the motor to determine torque power.
     */    
    private static final int MAX_POWER = 100;
 
    /**
     * Used by the TachoNavigator to determine speed.
     */    
    private static final int ONE_REV = 360;
    
    /**
     * Used by the TachoNavigator to determine speed.
     */    
    private static final int TWO_REVS = 720;

    /**
     * Used by the TachoNavigator to determine speed.
     */    
    private static final int MAX_REVS = 900;
    
    /**
     * Records number of completed missions.
     */    
    public static int COMPLETED_MISSIONS = 0;
    
    /**
     * Flags whether robot is in base.
     */    
    public static boolean IN_BASE = true;
 
    /**
     * Controls robot movements with differential steering.
     */
    public static Pilot pilot;
    
    /**
     * Tracks robot position, direction angle, and uses a Pilot object to
     * control the robot's movements.
     */
    public static TachoNavigator robot;
    
    /**
     * Controls which behavior should be active.
     */
    public static Arbitrator arbitrator;
    
    /**
     * An array of Behavior objects that will be passed to the Arbitrator.
     */    
    private static Behavior [] bArray;
    
    /**
     * The main method drives the mission.
     * 
     * @param args
     */
    public static void main(String[] args)
    {
        addBehaviors();
        createArbitrator();
        setMotorPower(MAX_POWER, MAX_POWER);
        regulateSpeed(true, true);
        setAcceleration(true, true);
        createNavigator();
        setSpeed(TWO_REVS); // 720 degrees per second
        
        // Robot begins at origin, facing due north
        setPosition(0, 0, 90);
        arbitrator.start();
    }
    
    /**
     * Populates an array of Behavior objects.
     */      
    private static void addBehaviors()
    {
        Behavior b1 = new LeaveBase(); // This class is in the same file
        Behavior b2 = new DriveForward(); // This class is in the same file        
        Behavior b3 = new MakeContact(); // This class is in the same file
        Behavior b4 = new Plough(); // This class is in the same file
        bArray = new Behavior [] {b1, b2, b3, b4};         
    }

    /**
     * Instantiates Arbitrator with an array of Behaviors.
     */      
    private static void createArbitrator()
    {
        arbitrator = new Arbitrator(bArray); 
    }

    /**
     * Instantiates Pilot and passes Pilot to TachoNavigator.
     */     
    private static void createNavigator()
    {
        pilot = new Pilot(WHEEL_DIAMETER, TRACK_WIDTH, Motor.A, Motor.B);
        robot = new TachoNavigator(pilot);
    }

    /**
     * Turns speed regulation on.
     */       
    private static void regulateSpeed(boolean A, boolean B)
    {
        Motor.A.regulateSpeed(A);
        Motor.B.regulateSpeed(B);
    }

    /**
     * Sets smooth acceleration.
     */       
    private static void setAcceleration(boolean A, boolean B)
    {
        Motor.A.smoothAcceleration(A);
        Motor.B.smoothAcceleration(B);
    }

    /**
     * Sets power rating for motors.
     */     
    private static void setMotorPower(int A, int B)
    {
        Motor.A.setPower(A);
        Motor.B.setPower(B);
    }

    /**
     * Sets robot position and direction for TachoNavigator.
     */     
    private static void setPosition(int X, int Y, int Z)
    {
        robot.setPosition(X, Y, Z);
    }

    /**
     * Sets robot speed in wheel revolutions per second.
     */     
    private static void setSpeed(int revolutions)
    {
        robot.setSpeed(revolutions);
    }
}

/**
 * Behaviour implemented to push scoring objects across Power field.
 * 
 * @version 2.0
 * @author Team Hadaga
 */
class Plough implements Behavior
{
    /**
     * Instantiates object/
     */ 
    public Plough() {}

    /**
     * Determines combination of variables that cause Arbitrator to assert
     * this Behaviour.
     */     
    public boolean takeControl()
    {
        // Implemented when missions completed equals 2 and robot in base.
        return ((PowerMissions.COMPLETED_MISSIONS > 0)
            && PowerMissions.IN_BASE);
    }

    /**
     * Describes how the robot behaves when this code is asserted.
     */       
    public void action()
    {
        // Delivers Grid/Wind.Turbines/Trees.
        if (PowerMissions.COMPLETED_MISSIONS == 1)
        {
            // Robot is manually set in position.
            PowerMissions.robot.setPosition(-80, 0, 90);
            
            // Delivers Grid/Wind Turbines/Trees.
            PowerMissions.robot.goTo(-40, 780);

            // Returns to base.
            PowerMissions.robot.travel(-200);
            PowerMissions.robot.rotateTo(270);
            PowerMissions.robot.goTo(-100, 0);      
        }
        
        // Robot pauses to be manually repositioned before botton pressed to
        // re-deploy.
        PowerMissions.robot.stop();
        while(Button.waitForPress() != 1)
        {
        }
        
        // Iterates mission trackers.
        PowerMissions.COMPLETED_MISSIONS++;
        PowerMissions.IN_BASE = true;
        
        // Delivers hydro-dam and parks in ocean to complete mission as Wave
        // Turbine.
        if (PowerMissions.COMPLETED_MISSIONS == 2)
        {
            PowerMissions.robot.setPosition(-230, 0,45);
            PowerMissions.robot.goTo(410, 580);
            PowerMissions.robot.travel(-200);
            PowerMissions.robot.rotateTo(180);
            PowerMissions.robot.goTo(0, 200);
            PowerMissions.robot.goTo(-600, 200);
            PowerMissions.robot.goTo(-1480, 200);
            PowerMissions.robot.goTo(-1480, 830);
            System.exit(0);
        }
    }

    /**
     * Describes how the robot reacts when another Behaviour is asserted.
     */      
    public void suppress()
    {
        PowerMissions.robot.stop();
    }
    
}
/**
 * Behavior is implemented when the robot departs from a destination.
 * 
 * @version 2.0
 * @author Team Hadaga
 */
class DriveForward implements Behavior
{
    /**
     * Instantiates object/
     */     
    public DriveForward() {}
    
    /**
     * Determines combination of variables that cause Arbitrator to assert
     * this Behaviour.
     */      
    public boolean takeControl()
    {
        // Implemented when robot leaves base for the first time.
        return (PowerMissions.COMPLETED_MISSIONS == 0
            && !(PowerMissions.IN_BASE));
    }

    /**
     * Describes how the robot behaves when this code is asserted.
     */    
    public void action()
    {
        PowerMissions.robot.forward();
    }

    /**
     * Describes how the robot reacts when another Behaviour is asserted.
     */      
    public void suppress()
    {
        PowerMissions.robot.stop();
    }
}

/**
 * This Behavior is asserted when the robot is at the origin.
 * 
 * @version 1.2
 * @author Team Hadaga
 */
class LeaveBase implements Behavior
{
    /**
     * Instantiates object/
     */       
    public LeaveBase() {}

    /**
     * Determines combination of variables that cause Arbitrator to assert
     * this Behaviour.
     */     
    public boolean takeControl()
    {
        return ((PowerMissions.robot.getX() == 0)
            && (PowerMissions.robot.getY() == 0)
            && PowerMissions.IN_BASE);
    }

    /**
     * Describes how the robot behaves when this code is asserted.
     */     
    public void action()
    {
        PowerMissions.robot.goTo(0, 100);
        
        if (PowerMissions.COMPLETED_MISSIONS == 0) 
        {
            PowerMissions.robot.rotateTo(85);          
        }
        else
        {
            // Can be used to assert Behaviour in other situations.
        }
        PowerMissions.IN_BASE = false;
    }

    /**
     * Describes how the robot reacts when another Behaviour is asserted.
     */      
    public void suppress()
    {
        PowerMissions.robot.stop();
    }
}

/**
 * This Behavior is asserted when the TouchSensor is activated.
 * 
 * @version 1.2
 * @author Team Hadaga
 */
class MakeContact implements Behavior, SensorPortListener
{
    /**
     * Receives pressure data from sensor.
     */    
    TouchSensor touch;

    /**
     * Records state of TouchSensor.
     */    
    boolean hasCollided;

    /**
     * Instantiates object/
     */        
    public MakeContact()
    {
        hasCollided = false;
        SensorPort.S1.addSensorPortListener(this);
    }

    /**
     * Determines combination of variables that cause Arbitrator to assert
     * this Behaviour.
     */         
    public boolean takeControl()
    {
        // Asserts control when SensorPort state changes.
        if ((hasCollided) && PowerMissions.COMPLETED_MISSIONS < 2) {
            // Resets variable value
            hasCollided = false;
            // returns true so Arbitrator can assert Behaviour.
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Describes how the robot behaves when this code is asserted.
     */    
    public void action()
    {
        // Deploys satellites.
        if (PowerMissions.COMPLETED_MISSIONS == 0)
        {
            // Push a little just to be sure Satellite 1 has been deployed.
            PowerMissions.robot.travel(10);
            PowerMissions.robot.travel(-150);

            PowerMissions.robot.rotateTo(270);
            
            // Transcribe an arc to satellite 2.
            PowerMissions.robot.turn(-420, 190);
            
             // Deploy satellite 2.
            PowerMissions.robot.travel(280);
            
            // Return to base.
            PowerMissions.robot.travel(-150);
            PowerMissions.robot.rotateTo(290); // face south
            PowerMissions.robot.goTo(-100, -100); // overshoot trip home a little
        }
        
        // Pauses and waits for button to be pressed before continuing.
        PowerMissions.robot.stop();
        while(Button.waitForPress() != 1)
        {
        }
        
        // Increments match trackers.
        PowerMissions.COMPLETED_MISSIONS++;
        PowerMissions.IN_BASE = true;
    }

    /**
     * Describes how the robot reacts when another Behaviour is asserted.
     */      
    public void suppress()
    {
        PowerMissions.robot.stop();
    }

    /**
     * Monitors and reports pressure applied against TouchSensor.
     */     
    public void stateChanged(SensorPort touch, int oldValue, int newValue)
    {
        // If the sensor is pressed with a 900/1024 pressure
        if (newValue < 900)
        {
            // Asserts takeControl()
            hasCollided = true;
        }
    }
}

