/* floor.cpp
 *
 * A floor maintains a pool of attendees.
 */


#include <iostream.h>
#include <stdlib.h>
#include <math.h>
#include "floor.H"
#include "person.H"
#include "control.H"
#include "events.H"
#include "sched.H"
#include "main.H"
#include "opengl.H"
#include "graphics.H"
#include "list.H"


int Floor::offsets[][2] = {	// offsets at which to place waiting persons
    {-1,1}, {1,2}, {0,0}, {2,1}, {-2,1}, {-1,0}, {-1,3}, {2,3}, {1,1},
    {4,0}, {-3,2}, {0,2}, {4,2}, {-3,0}, {2,0}, {-2,3}, {1,3}, {3,1},
    {3,3}, {-1,2}, {0,3}, {-4,1}, {-4,3}, {3,0}, {4,1}, {-2,0}, {0,1},
    {-3,1}, {-2,2}, {3,2}, {-3,3}, {2,2}, {-4,2}, {1,0}, {-4,0}, {4,3},
    {0,-1} }; // {0,-1} marks end

int Floor::max_xoffset = 4;	// max and min in of x components
int Floor::min_xoffset = -4;
int Floor::num_offsets = 36;

/* Return the index (floor number) of this floor.
 */

int 
Floor::index()
{
    return floor_num;
}


/* Add one person to the pool of attendees.
 */

void 
Floor::add_person( Person *pers )
{
    attendees.add_data( pers );
}


/* Remove a person from the pool of attendees.
 */

void 
Floor::remove_person( Person *pers )
{
    attendees.remove_data( pers );
}


/* Signal all the attendees that an elevator has arrived
 */

void 
Floor::signal_arrival_to_attendees( Elevator *elev )
{
    Lnode *n, *nn;

    /* Loop through the attendees.  One subtlety: When a person is
     * signalled of the arrival of an elevator, they might remove
     * themselves from the attendees list, so we need to know what the
     * next node of the list is BEFORE signalling the person.  */

    n = attendees.get_next(NULL); 
    while (n != NULL) {
	nn = attendees.get_next( n );
	(n->data)->signal_arrival( elev, this );
	n = nn;
    }
}


/* Request that an elevator arrive to go to a particular floor.  The
 * floor passes the request along to the elevator controller, along
 * with its own (the floor's) identity.
 */

void 
Floor::request_elevator( Floor *floor )
{
    controller->request_elevator( this, floor );
}


/* Schedule the next arrival of a person at this floor.
 */

void 
Floor::schedule_next_appearance()
{
    PersonAppearance *event;
    float appearance_time;
    Floor *dest_floor;
    float rand_val;
    int   next_floor_num;

    // Choose the next appearance time

    appearance_time = current_time + mean_arrival_time + 
	((double)rand()/RAND_MAX - 0.5) * mean_arrival_time;


    // Choose next destination floor.  

    do {
        // choose each floor with equal probability not realistic.
	rand_val = (double)rand()/RAND_MAX;
	
	next_floor_num = (int) floor( rand_val * num_floors );
    } while (next_floor_num == num_floors || next_floor_num == floor_num);

    // Create the event and add it to the event queue

    event = new PersonAppearance( appearance_time, this,
				  &floors[ next_floor_num ] );

    scheduler.add_event( event );
}


/* Draw the floor.  The back-centre of the floor appears at (x,y,z).
 */

void
Floor::draw( float x, float y, float z )
{
#ifdef GL

    int i, start_i;
    float ex, ey, ez;
    float xbase, xshift;
    Lnode *n;

    graphics.black();

    // Draw the floor

    graphics.white();
    glBegin( GL_POLYGON );
    glVertex3f( x-graphics.floor_width/2.0, y, 0 );
    glVertex3f( x+graphics.floor_width/2.0, y, 0 );
    glVertex3f( x+graphics.floor_width/2.0, y, z+graphics.floor_depth );
    glVertex3f( x-graphics.floor_width/2.0, y, z+graphics.floor_depth );
    glEnd();
    graphics.black();
    glBegin( GL_LINE_LOOP );
    glVertex3f( x-graphics.floor_width/2.0, y, 0 );
    glVertex3f( x+graphics.floor_width/2.0, y, 0 );
    glVertex3f( x+graphics.floor_width/2.0, y, z+graphics.floor_depth );
    glVertex3f( x-graphics.floor_width/2.0, y, z+graphics.floor_depth );
    glEnd();

    // draw the elevator doors

    for (i=0; i<num_elevators; i++) {

	ex = (graphics.elev_width + graphics.elev_sep) *
	    (i - (float)(num_elevators - 1) / 2.0);
	ey = y;
	ez = z;

	glBegin( GL_LINE_STRIP );
	glVertex3f(ex-graphics.elev_width/2.4, ey, ez );
	glVertex3f(ex-graphics.elev_width/2.4, ey+graphics.elev_height*0.9,ez);
	glVertex3f(ex+graphics.elev_width/2.4, ey+graphics.elev_height*0.9,ez);
	glVertex3f(ex+graphics.elev_width/2.4, ey, ez );
	glEnd();
    }

    /* Draw the waiting persons.  They're laid out according to the
     * offsets[][] array.  The end of the array is marked with a {0,-1}
     * entry.  After that, we use the same offsets, but starting from an
     * xbase to the left or right of all the currently drawn persons.
     * To make positions appear a bit more random, we start at a position
     * in the offsets array that depends upon the floor number. */

    xbase = x;
    xshift = 0;

    start_i = (floor_num * 7) % num_offsets;
    i = start_i;

    for (n = attendees.get_next(NULL); n != NULL; n = attendees.get_next(n)) {

	i = (i+1) % num_offsets;

	if (i == start_i) {
	    if (xshift == 0)	// xshift takes on values in the sequence
		xshift = 1;	// 0,1,-1,2,-2,3,-3,4,-4,...
	    else if (xshift > 0)
		xshift = -xshift;
	    else
		xshift = -xshift + 1;
	    xbase = x + xshift * (max_xoffset - min_xoffset + 1) *
		graphics.person_sep * 1;
	}

	(n->data)->draw( xbase + graphics.person_sep * offsets[i][0] * 2 
			 + graphics.person_sep *
			 ((double)rand()/RAND_MAX - 0.5) * 0.3, y, 
			 z + graphics.floor_depth - graphics.person_sep *
			 (offsets[i][1] + 1)
			 + graphics.person_sep *
			 ((double)rand()/RAND_MAX - 0.5) * 0.3);
    }

#endif
}


