/* controller.cpp
 */


#include <math.h>
#include "control.H"
#include "elevator.H"
#include "floor.H"
#include "events.H"
#include "sched.H"


/* The constructor just initializes lots of arrays
 */

Controller::Controller( int num_floors, int num_elevators )
{
    int i, j;
    
    // Initialize the array of travel requests; none at first

    for (i=0; i<num_floors; i++) {
	for (j=0; j<num_floors; j++)
	    requests[i][j] = 0;
    }

    // Allocate the array of locations; initial positions chosen using
    // an arbitrary function.
    
    for (i=0; i<num_elevators; i++)
	locations[i] = (i*7) % num_floors;

    // Allocate the array of destinations; none initially
    
    for (i=0; i<num_elevators; i++) {
	for (j=0; j<num_floors; j++)
	    destinations[i][j] = 0;
    }

    // Allocate the array of directions: all IDLE initially

    for (i=0; i<num_elevators; i++)
	directions[i] = IDLE;
}


/* Return 1 if the floor is a current destination of the elevator; 0
 * otherwise.
 */

int
Controller::is_a_destination( Elevator *elev, Floor *floor )
{
    return (destinations[ elev->index() ][ floor->index() ] != 0);
}


/* Called upon a request that an elevator come to the source floor to
 * carry someone to the dest floor.
 *
 * This is one of the functions in which the elevator algorithm goes.
 */


void
Controller::request_elevator( Floor *source, Floor *dest )
{
    int i;

    // Record the request

    requests[ source->index() ][ dest->index() ] ++;

    // Send an elevator, if possible.  In this simple algorithm, we just
    // start up the idle elevators.

    for (i=0; i<num_elevators; i++)
	if (directions[i] == IDLE) {
	    
	    if (locations[i] == num_floors-1)
		directions[i] = UP;
	    else
		directions[i] = DOWN;
	    
	    update_destinations( &elevators[i], &floors[ locations[i] ] );
	    
	    schedule_next_departure( &elevators[i] );
	}
}


/* Update the list of destinations of this elevator.  This must
 * be done *before* the people are told of the arrival, so that
 * they can know whether to get on or not.
 *
 * This is one of the functions in which the elevator algorithm goes.
 */


void
Controller::update_destinations( Elevator *elev, Floor *floor )  
{
    int i, e, f, next_f;

    e = elev->index();
    f = floor->index();
  
    destinations[ e ][ f ] = 0;	// This floor is no longer a destination
    locations[ e ] = f;		// Record elevator's current location

    /* Determine the next floor.  This just looks through the list of 
     * destinations for the elevator in the current direction.  If there's
     * no next floor, this sets next_f to -1.
     */

    switch ( directions[e] ) {

    case UP:
	// see if there are requests for the current direction 
	for(i = f + 1; i < num_floors; i++) 
	    if(requests[f][i] != 0)
		destinations[e][i] = 1;

	// find the next floor
	for(i = f; i < num_floors; i++)
	    if (destinations[e][i] != 0)
		break;
	if(i < num_floors)
	    next_f = i;
	else
	    next_f = -1;
	break;

    case DOWN:
	
	for(i = f - 1; i >= 0; i--) 
	    if(requests[f][i] != 0)
		destinations[e][i] = 1;

	for(i = f; i >= 0; i--)
	    if (destinations[e][i] != 0)
		break;
	if(i >= 0)
	    next_f = i;
	else
	    next_f = -1;
	break;
    }

    if (next_f == -1) {

	switch( directions[e] ) {
      
	case UP:

	    directions[e] = DOWN;
	    next_f = f - 1;
	    for (i = next_f; i >= 0; i--)
		if(requests[f][i] != 0)
		    destinations[e][i] = 1;
	    
	    for(i = next_f; i >= 0; i--)
		if (destinations[e][i] != 0) 
		    break;
		
	    next_f = i;

	    if (next_f < 0)
		next_f = 0;
	    break;
	    
	case DOWN:
	    
	    directions[e] = UP;
	    next_f = f + 1;
	    for (i = next_f; i < num_floors; i++)
		if(requests[f][i] != 0)
		    destinations[e][i] = 1;

	    // find the next floor
	    for(i = next_f; i < num_floors; i++)
		if (destinations[e][i] != 0) 
		    break;
	    next_f = i;
	    
	    if (next_f > num_floors-1)
		next_f = num_floors-1;
	    break;
	}
    }
    
    /* Tell the elevator about its next departure time. */
    
    elev->next_floor = &floors[ next_f ];
    elev->departure_time = current_time + elev_delay;
    
    /* Assume that we'll satisfy all requests from this floor to one of
     * our destination floors.  If people can't get on, they'll make
     * another request.
     */
    
    for (i=0; i<num_floors; i++)
	if (destinations[e][i] != 0)
	    requests[f][i] = 0;
}


/* Schedule the next departure of this elevator.
 */

void
Controller::schedule_next_departure( Elevator *elev )
{
    ElevatorDeparture *event;
    
    event = new ElevatorDeparture(elev->departure_time,elev, elev->next_floor);
    scheduler.add_event( event );

    /* Temporarily set the destination to the current floor ... until the
     * elevator actually departs.  This is necessary to keep track
     * of the elevator's position.
     */
    
    elev->next_floor = elev->last_floor;
}


/* Schedule the next stop for this elevator (or none, if it is
 * to remain idle).
 */

void 
Controller::schedule_next_arrival( Elevator *elev, Floor *floor )
{
    ElevatorArrival *event;
    float travel_time;

    elev->next_floor = floor;

    //Compute the travel time (see Elevator::floor_position() for a derivation)
    travel_time  = sqrt( 4 * fabs(elev->next_floor->index() -
				  elev->last_floor->index())
			 / elev->accel_rate );

    // Schedule the next arrival
    event = new ElevatorArrival( current_time + travel_time, elev, floor );

    scheduler.add_event( event );
}

 
/* Help to debug the controller by printing the contents of the
 * various arrays.
 */

void
Controller::debug()
{
    int i,j;

    // travel requests

    cout << "-- Travel requests --\n";
    cout << "    To: ";
    for (j=0; j<num_floors; j++)
	cout << j << " ";
    cout << "\n";

    for (i=0; i<num_floors; i++) {
	cout << "From " << i << ": ";
	for (j=0; j<num_floors; j++)
	    cout << requests[i][j] << " ";
	cout << "\n";
    }

    // locations & direction

    cout << "-- Status --\n";
    cout << "Elevator:  ";
    for (i=0; i<num_elevators; i++)
	cout << i << " ";
    cout << "\n";

    cout << "Location:  ";
    for (i=0; i<num_elevators; i++)
	cout << locations[i] << " ";
    cout << "\n";

    cout << "Direction: ";
    for (i=0; i<num_elevators; i++)
	switch (directions[i]) {
	case UP:    cout << "U "; break;
	case DOWN:  cout << "D "; break;
	case IDLE:  cout << "I "; break;
	}
    cout << "\n";

    cout << "Capacity:  ";
    for (i=0; i<num_elevators; i++)
	cout << elevators[i].capacity << " ";
    cout << "\n";

    // destinations

    cout << "-- Destinations --\n";
    cout << "Elevator: ";
    for (i=0; i<num_elevators; i++)
	cout << i << " ";
    cout << "\n";

    for (j=0; j<num_floors; j++) {
	cout << "Floor " << j << ":  ";
	for (i=0; i<num_elevators; i++)
	    cout << destinations[i][j] << " ";
	cout << "\n";
    }
}
