// commands as transmitted from the
// desktop to the tanks
#define FWD	0
#define REV	1
#define LEFT	2
#define RIGHT	3
#define STOP	4
#define FIRE	5

// broadcast message sent from the
// desktop, inviting tanks to sign
// up with the desktop
#define HELLO	0x10

// define threshold for the light sensor.
// If the readings of the sensor go above
// that value, we have probably been hit
// by the laser of another tank. Note: the
// light sensor gets confused by flashlight
// photography
#define LASER_THRESHOLD 70

// the three different status messages that
// we can sent back to the desktop.
#define STATUS_OK       0x40
#define STATUS_BUMPED	0x41
#define STATUS_KILLED	0x42

// The ID of a particular tank. At the moment,
// we have to change this value for each tank,
// and recompile and download. This will be 
// changed ASAP.
#define MY_ID			0x61
#define SEND_ID			(MY_ID + 0x20)

int alive;   		// are we still alive?
int fire_sem;		// is a fire cmd currently in process
int id;			// ID associated with current cmd
int status;		// current status
int cmd;		// current cmd

// the main tank task. This initially
// waits for an invitation to sign up
// from the host (the HELLO message).
// The tank responds by sending its ID.
// After that, the task sits in a loop
// waiting for commands that are addressed
// to this tank. The host will prepend
// each cmd with a tank ID, so the tanks
// will know which cmd is meant for which
// tank. The directional cmds (fwd, bwd,
// left, right, stop) just manipulate the
// direction of the motors driving the 
// tank treads, and complte almost 
// immediately. The fire cmd is more time-
// consuming, and hence is spun off in
// its own task (see below).
task main {
	fire_sem = 0;
	status = STATUS_OK;
	cmd = 0;

	start bump_task;
	start hit_task;
	
	while (true) {
	
		alive = 1;
		
		while (cmd != HELLO) {
			ClearMessage();
			wait (Message() != 0);
			cmd = Message();
		}
		
		SendMessage(SEND_ID);
		
		// supply audio confirm
		// to user
		repeat (2) {
			PlaySound(1);
			Sleep(100);
		}

		while (true) {
			ClearMessage();
			wait (Message() != 0 || alive == 0);
			if (alive == 0) break;

			// ignore all messages
			// not meant for us
			id = Message();
        		if (id != MY_ID) continue;

			ClearMessage();
			wait (Message() != 0 || alive == 0);
			if (alive == 0) break;
        		cmd = Message() - 0x20;

	    		SendMessage(SEND_ID);
			SendMessage(status);
			status = STATUS_OK;
        
			if (cmd == FWD) {
				Fwd(OUT_A + OUT_C, 4);
			}

			if (cmd == REV) {
				Rev(OUT_A + OUT_C, 4);
			}

			if (cmd == LEFT) {
				Fwd(OUT_C, 4);
				Rev(OUT_A, 4);
			}

			if (cmd == RIGHT) {
				Fwd(OUT_A, 4);
				Rev(OUT_C, 4);
			}

			if (cmd == STOP) {
				Off(OUT_A + OUT_C);
			}

			if (cmd == FIRE && fire_sem == 0) {
				fire_sem = 1;
				start fire_task;
			}
		}
	}
	
	stop bump_task;
	stop hit_task;
	Off(OUT_A + OUT_C);
}

// This task waits for either the
// front bumper or the back bumper
// to activate. It will then briefly
// switch direction for both treads,
// and finally stop. The brief switch
// is needed to ensure that the bumper
// will unblock. A blocked bumper will
// keep the tank from operating (because
// the bumper behavior just described
// will execute over and over).
task bump_task {
	Sensor(IN_1, IN_SWITCH);
	Sensor(IN_3, IN_SWITCH);

	while (true) {
		ClearSensor(IN_1);
		ClearSensor(IN_3);

		wait (IN_1 == 1 || IN_3 == 1);		

		OutputDir(OUT_A + OUT_C, OUT_FLIP);
		Sleep(30);
		Off(OUT_A + OUT_C);
		
		if (status == STATUS_OK) status = STATUS_BUMPED;
	}
}

// This  task waits for the light
// sensor to report input that lies
// above the specified threshold. 
// When that condition is met, it 
// gives audio confirmation of the fact
// and clears the 'alive' flag.
task hit_task {
	Sensor(IN_2, IN_LIGHT);
	
	while (true) {
		ClearSensor(IN_2);

		wait (IN_2 > LASER_THRESHOLD);

		status = STATUS_KILLED;
		Off(OUT_A + OUT_C);

		repeat (3) {
			PlaySound(2);
			Sleep(100);
		}
	
		alive = 0;
	}
}

// this task drives the motor that
// operates the cam, which activates
// the laser gun, in one direction 
// for 1500 milliseconds. It then
// briefly reverses the direction 
// to make sure the laser will turn off.
task fire_task {
	Fwd(OUT_B, 1);
	
	repeat(3) {
		PlaySound(5);
		Sleep(50);
	}
	
	Rev(OUT_B, 1);
	Sleep(20);
	Off(OUT_B);
	fire_sem = 0;
}