/* person.cpp
 *
 * A person is scheduled to appear on a particular floor at a
 * particular time.  When the person arrives, the floor causes the
 * *next* arrival to be scheduled.
 */

#include <stdlib.h>
#include <math.h>
#include "person.H"
#include "elevator.H"
#include "list.H"
#include "stats.H"
#include "graphics.H"
#include "main.H"
#include "control.H"
#include "sched.H"


int Person::num_persons = 0;	// current number of persons


/* Return the index of the person.
 */

int
Person::index()
{
    return person_num;
}

/* Instantiate a person who wants to go from a source floor to a
 * destination floor.  Add the person to the source floor and request
 * an elevator to the destination floor.
 */

void 
Person::handle_initial_appearance( Floor *source, Floor *dest )
{
    source_floor = source;
    dest_floor = dest;
    start_time = current_time;
    status = ON_FLOOR;

    source_floor->add_person( this );
    source_floor->request_elevator( dest );
}


/* Signal to the person that an elevator has arrived.  
 *
 * If the person is on a floor:
 *   If the elevator is going to this person's destination floor then
 *   if it has capacity, the person gets into the elevator and if it
 *   doesn't have capacity, the person makes another elevator request.
 * If the person is in the elevator:
 *   If the current floor is the person's destination floor, the
 *   person gets out and reports their arrival for the statistics.
 */

void
Person::signal_arrival( Elevator *elev, Floor *floor )
{
    if (text_output) {
	cout << current_time << ": Person ";
	this->print();
    }

    switch (status) {

    case ON_FLOOR:

	if ( elev->is_a_destination( dest_floor ) ) {
	    // elevator is going to dest floor
	    if (elev->space_left() == 0) {		  // no more space
		if (text_output)
		    cout << " waits for next elevator (this one's full)\n";
		floor->request_elevator( dest_floor );
	    } else {			// get into the elevator
		if (text_output) {
		    cout << " gets into elevator ";
		    elev->print();
		    cout << "\n";
		}
		floor->remove_person( this );
		elev->add_person( this );
		status = IN_ELEVATOR;
	    }
	} else
	    if (text_output)
		cout << " waits for next elevator (this one's going elsewhere)\n";
	
	break;
	
    case IN_ELEVATOR:
	if( floor == dest_floor ) { // at the dest floor yet?
	    if(text_output) {
		cout << " gets out of elevator ";
		elev->print();
		cout << "\n";
	    }
	    elev->remove_person( this );
	    statistics.report_arrival( this );
	} else
	    if(text_output)
		cout << " stays in elevator (this isn't the destination yet)\n";
	break;
	
    default:
	cerr << "signal_arrival: Reached default in switch\n";
    }
}

/* Print a person to stdout
 */

void
Person::print()
{
    cout << this->index();
}


/* Draw a person.  The person's feet will be centred at (x,y,z).
 */

void
Person::draw( float x, float y, float z )
{
#ifdef GL
    float lx, mx, rx, ty, my, by;

    lx = x - graphics.person_width/2.0;
    mx = x;
    rx = x + graphics.person_width/2.0;

    by = y + graphics.person_height * 0.25;
    my = y + graphics.person_height * 0.75;
    ty = y + graphics.person_height;

    /* Colour the person according to their direction
     * of travel: up = red, down = blue.
     */

    if (source_floor->index() < dest_floor->index())
	graphics.red();
    else
	graphics.blue();

    /* Draw the person */

    glBegin( GL_LINE_STRIP );	// body
    glVertex3f( mx, by, z );
    glVertex3f( mx, ty, z );
    glEnd();

    glBegin( GL_LINE_STRIP );	// arms
    glVertex3f( lx, my, z );
    glVertex3f( rx, my, z );
    glEnd();

    glBegin( GL_LINE_STRIP );	// left leg
    glVertex3f( lx, y, z );
    glVertex3f( mx, by, z );
    glEnd();

    glBegin( GL_LINE_STRIP );	// right leg
    glVertex3f( rx, y, z );
    glVertex3f( mx, by, z );
    glEnd();
#endif
}

ImpatientPerson::ImpatientPerson(Floor *floor) : Person()
{
    /* The the time an impatient person will wait before leaving (taking
     * the stairs) is exponentially distributed
     */
    
    give_up_time = (double) (- mean_wait_time) * log((double)rand()/RAND_MAX );
    give_up_time += current_time;

    /* We will schedule the person's departure.  If an elevator comes before
     * the person leaves in impatience, the PersonLeave event will be removed
     * from the event queue.
     */
    leave_event = new PersonLeave( give_up_time, floor, this);
    scheduler.add_event(leave_event);
}

void
ImpatientPerson::signal_arrival(Elevator *elev, Floor *floor)
{
    if (text_output) {
	cout << current_time << ": Impatient Person ";
	this->print();
    }

    switch (status) {

    case ON_FLOOR:
	if ( elev->is_a_destination( dest_floor ) ) {
	    // elevator is going to dest floor
	    if (elev->space_left() == 0) {		  // no more space
		if (text_output)
		    cout << " waits for next elevator (this one's full)\n";
		floor->request_elevator( dest_floor );
	    } else {			// get into the elevator
		if (text_output) {
		    cout << " gets into elevator ";
		    elev->print();
		    cout << "\n";
		}
		floor->remove_person( this );
		elev->add_person( this );
		status = IN_ELEVATOR;
		// delete the event that would cause the impatient person
	        // to leave without getting on an elevator.
		scheduler.delete_event(this->leave_event);
	    }
	} else
	    if (text_output)
		cout << " waits for next elevator (this one's going elsewhere)\n";
	
	break;
	
    case IN_ELEVATOR:
	if( floor == dest_floor ) { // at the dest floor yet?
	    if(text_output) {
		cout << " gets out of elevator ";
		elev->print();
		cout << "\n";
	    }
	    elev->remove_person( this );
	    statistics.report_arrival( this );
	    statistics.report_impatient();
	} else
	    if(text_output)
		cout << " stays in elevator (this isn't the destination yet)\n";
	break;
	
    default:
	cerr << "signal_arrival: Reached default in switch\n";
    }
}

void
ImpatientPerson::draw( float x, float y, float z )
{
#ifdef GL
    float lx, mx, rx, ty, my, by;

    lx = x - graphics.person_width/2.0;
    mx = x;
    rx = x + graphics.person_width/2.0;

    by = y + graphics.person_height * 0.25;
    my = y + graphics.person_height * 0.75;
    ty = y + graphics.person_height;

    /* All impatient people are coloured green */

    graphics.green();

    /* Draw the person */

    glBegin( GL_LINE_STRIP );	// body
    glVertex3f( mx, by, z );
    glVertex3f( mx, ty, z );
    glEnd();

    glBegin( GL_LINE_STRIP );	// arms
    glVertex3f( lx, my, z );
    glVertex3f( rx, my, z );
    glEnd();

    glBegin( GL_LINE_STRIP );	// left leg
    glVertex3f( lx, y, z );
    glVertex3f( mx, by, z );
    glEnd();

    glBegin( GL_LINE_STRIP );	// right leg
    glVertex3f( rx, y, z );
    glVertex3f( mx, by, z );
    glEnd();
#endif
}
