/* File: network.cpp
 *
 * The functions local to the network class and its subclasses: Street,
 *    Intersection, and TrafficLight
 */

#include "queues.h"                  // a queue datatype
#include "graph.h"                   // a graph datatype
#include "network.h"                 // the network interface
#include "random_numbers.h"          // random number generator
#include "shortest_paths.h"          // compute the shortest path

#define DEFAULT_LIGHT_CYCLE 240      // default cycle time for a traffic light


/*
 * add_street() - adds a street from intersection x to y.  length is the
 *     minimum time it will take a car to traverse the street, capacity is
 *     the maximum number of cars the street may hold, and speed is the
 *     maximum number of cars that may exit the street per clock tick.
 */
void Network::add_street(int x, int y, int length, int capacity, int speed)
{
    if (streets[x][y].queue == 0)            // if the street does not exist
    {
        ******ADD EDGE (x,y,length) TO graph **********
        graph_modified = true;               // and note that the graph changed
        streets[x][y].from = x;              // update the street info
        streets[x][y].to = y;
        streets[x][y].speed = speed;
        streets[x][y].length = length;
        streets[x][y].capacity = capacity;
        streets[x][y].load = 0;
        streets[x][y].queue = ****CREATE A NEW QUEUE OF SIZE capacity*****
        num_streets++;
    }
}

/*
 * add_intersection() - sets the parameters of a network intersection.
 *    There are at most 4 streets at an intersection, and these are 
 *    designated as "north", "south", "east", and "west".  The value is the
 *    intersection where each street originated.
 */
void Network::add_intersection(int x, int north, int east, int south, int west)
{
    intersections[x].light.initialize_light(north, east, south, west, 
                                            DEFAULT_LIGHT_CYCLE);
}

/*
 * start() - start up the network.  start_time is the current time.
 */
void Network::start(int start_time)
{
    Street **street_array_ptr;        // used to organize the streets

                                      // put each light in a random state
    for (int i = 0; i < num_intersections; i++)
        intersections[i].light.random_cycle(start_time);

    street_array = new Street * [num_streets];
    street_array_ptr = street_array;  // make a 1-D array of the streets so that
                                      // we can access them easily
    for (int i = 0; i < num_intersections; i++)
        for (int j = 0; j < num_intersections; j++)
            if (streets[i][j].queue != 0)
                *street_array_ptr++ = &(streets[i][j]);
}

/*
 * update() - update the street network to the current time.
 */
void Network::update(int time)
{                                       // the traffic lights must be updated
    for (int i = 0; i < num_intersections; i++)
        intersections[i].light.update_light(time);
}

/*
 * The Network constructor: we must allocate space for all the possible streets,
 *     intersections and underlying graph.  size is the number of intersections.
 */
Network::Network(int size)
{
    graph = ***** CREATE A GRAPH TO HOLD THE UNDERLYING GRAPH ******
    graph_modified = true;
    streets = new Street * [size];
    for (int i = 0; i < size; i++)
       streets[i] = new Street[size];
    intersections = new Intersection[size];
    num_intersections = size;
    num_streets = 0;
}

/*
 * The Network destructor: Free all allocated memory
 */
Network::~Network()
{
    ****** DEALLOCATE THE GRAPH ******
    for (int i = 0; i < num_intersections; i++)
        delete[] streets[i];
    delete[] streets;
    delete[] street_array;
    delete[] intersections;
}

/*
 * get_shortest_path() - get the shortest path from x to y in the network.
 *    The path is stored in return_path and the length is returned.
 */
int Network::get_shortest_path(int x, int y, int *return_path)
{
    int length;

    ****** FIND THE SHORTEST PATH, SET length TO HOLD ITS LENGTH AND
           return_path TO STORE THE PATH FOUND ***********
    graph_modified = false;          // now that we used the graph, reset the
    return length;                   // modified flag
}

/*
 * light_is_green() - returns whether the traffic light is green for this
 *     street.
 */
bool Network::light_is_green(Street *street)
{
    return (intersections[street->to].light.is_green(street->from));
}

/*
 * add_to_street() - add car, car_id, to a street.  Return whether the add
 *       was successful.
 */
bool Network::add_to_street(int car_id, Street *street)
{
    *****INSERT car_id TO street->queue***********
    *****IF YOU GET A QUEUE_FULL ERROR, RETURN false ELSE RETURN true *****
}

/*
 * next_to_go() - sets car_id to be the number of the head car on the street.
 *    and returns whether or not there is a car.
 */
bool Network::next_to_go(Street *street, int *car_id)
{
    ****SET *car_id TO BE THE VALUE FROM A PEEK OF street->queue****
    ****IF YOU GET A QUEUE_EMPTY ERROR, RETURN false ELSE RETURN true****   
}

/*
 * remove_from_street() - sets car_id to be the id of the next car to leave
 *    the street and returns true if a car did leave the street.
 */
bool Network::remove_from_street(Street *street, int *car_id)
{
    ****SET *car_id TO BE THE VALUE REMOVED FROM street->queue****
    ****IF YOU GET A QUEUE_EMPTY ERROR, RETURN false ELSE RETURN true****   
}

/*
 * is_green() - is the traffic light green for the road from intersection x?
 */
bool TrafficLight::is_green(int x)
{
    int i;
    int position = MAX_NEIGHBORS;

    for (i = 0; i < MAX_NEIGHBORS; i++) // neighbors stores the road in north,
        if (neighbors[i] == x)          // east, south, west order
            position = i;               // position is the order of x
    if (position == MAX_NEIGHBORS)      // if there is no such street....
        return false;                   // else we are green if the position is
    else if ((current_state == EVEN_GREEN) && (position % 2 == 0))
        return true;                    // 0 or 2 on an EVEN_GREEN or the
    else if ((current_state == ODD_GREEN) && (position % 2 == 1))
        return true;                    // position is 1 or 3 on an ODD_GREEN
    else
        return false;
}

/*
 * update_light() - update the traffic light to the current time
 */
void TrafficLight::update_light(int time)
{
    if (time >= next_change)               // is it time to change the light?
    {                                      // set new state & next change time
        current_state = (TrafficLight::state)((current_state + 1) % MAX_STATES);
        next_change = time + cycle_times[current_state];
    }
}

/*
 * random_cycle() - place the traffic light into a random state at the given
 *                  time.
 */
void TrafficLight::random_cycle(int time)
{
    current_state = (TrafficLight::state)get_discrete_random(MAX_STATES);
    next_change = time + get_discrete_random(cycle_times[current_state]) + 1;
}

/*
 * initialize_light() - set the north, south, east, west streets and the
 *      time for each part of the light cycle.
 */
void TrafficLight::initialize_light(int north, int east, int south, int west,
                                    int cycle)
{
    cycle_length = cycle;
    neighbors[0] = north;
    neighbors[1] = east;
    neighbors[2] = south;
    neighbors[3] = west;
    cycle_times[ALL_RED_1] = ALL_RED_TIME;   // currently the cycle is symmetric
    cycle_times[ALL_RED_2] = ALL_RED_TIME;
    cycle_times[EVEN_GREEN] = (cycle_length - 2 * ALL_RED_TIME) / 2;
    cycle_times[ODD_GREEN] = cycle_length 
                                 - 2 * ALL_RED_TIME - cycle_times[EVEN_GREEN];
}


