// hw7vc.cpp
//#include <random_generators.h> // not in this series

#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>

#include "vc_delay.h"  // or some delay routine
#include "vc_plotd.h"  // or some other plot package

// simulation.h
#ifdef VMS
#define CLOCKS_PER_SEC CLK_TCK
#endif
  int CLK_PER_SEC = CLOCKS_PER_SEC/2; // divide by bigger number for faster cars
  enum direction { north, east, south, west };
  enum signal_state { green, yellow, red , none, stop };
  float scale; // user must scale simulation to screen/window
  void initialize(void);
  int unique_id(void);
  void get_offsets(direction moving,
		   float &x_offset,
		   float &y_offset);
// end simulation.h

// simulation.cpp
static int next_id = 0;

void initialize(CDC *pDC)
{
    INITIALIZE(pDC);
}

int unique_id(void)
{
    next_id++;
    return next_id;
}

void get_offsets(direction moving,
		   float &x_offset,
		   float &y_offset)
{
    switch( moving )
    {
      case north:
	x_offset = 0.0F;
	y_offset = 1.0F;
	break;
      case  east:
	x_offset = 1.0F;
	y_offset = 0.0F;
	break;
      case south:
	x_offset = 0.0F;
	y_offset = -1.0F;
	break;
      case west:
	x_offset = -1.0F;
	y_offset = 0.0F;
    }
}
// end simulation.cpp


// vehicle.h
// #include <simulation.h>
class vehicle
{
  public:
     vehicle( float Alength,
	      float Aweight,
	      float Aaccel,
	      float Adecell,
	      float Aposition,
	      float Aspeed,
	      float Aspeed_limit,
	      float Ax_base,
	      float Ay_base,
	      direction Amoving,
	      float Asignal_pos,
	      vehicle *Anext_ptr);
    void move(void);
    vehicle *next(void);
    void set_signal(signal_state Asignal);
    void draw(void);
  private:
    int    id;
    float  length;
    float  weight;
    float  accel;
    float  decell;
    float  position;
    float  position_last;
    float  speed;
    float  speed_limit;
    float  time_last_move;
    float  x_base;
    float  y_base;
    direction  moving;
    signal_state  signal;
    float  signal_pos;
    vehicle  *next_ptr;
}; // end vehicle.h


// vehicle.cpp
vehicle::vehicle( float Alength,
	      float Aweight,
	      float Aaccel,
	      float Adecell,
	      float Aposition,
	      float Aspeed,
	      float Aspeed_limit,
	      float Ax_base,
	      float Ay_base,
	      direction Amoving,
	      float Asignal_pos,
	      vehicle *Anext_ptr)
{
     id = unique_id();
     length = Alength;
     weight = Aweight;
     accel =  Aaccel;
     decell = Adecell;
     position = Aposition;
     position_last = Aposition;
     speed = Aspeed;
     speed_limit = Aspeed_limit;
     time_last_move = (float)clock()/CLK_PER_SEC;
     x_base = Ax_base;
     y_base = Ay_base;
     moving = Amoving;
     signal = none;
     signal_pos = Asignal_pos;
     next_ptr = Anext_ptr;
}

void vehicle::move(void)
{
    float delta_time;
    float separation = 16.0F/10.0F; // 16 feet per 10 feet per second
    float speed_temp;
    float position_temp;
    float stopping_distance;

    delta_time = (float)clock()/CLK_PER_SEC - time_last_move;
    if(delta_time > 4.0) delta_time = 4.0F;
    time_last_move = (float)clock()/CLK_PER_SEC;
    position_last = position;
    speed_temp = speed;


    // attempt normal movement, accelerate to speed limit
    if (speed_temp < speed_limit)      // try to accelerate
    {
      speed_temp = speed + accel*delta_time;
      if (speed_temp > speed_limit)
      {
	speed_temp = speed_limit;
      }
    }
    position_temp = position + speed_temp*delta_time;

    // limit speed if necessary to keep distance from vehicle in front
    if (next_ptr != NULL)          // check what is in front ( if any )
    {
      if( position_temp + separation*speed_temp >
	 next_ptr->position - next_ptr->length)
      {
	speed_temp    = speed;
	position_temp = position + speed_temp*delta_time;
	if (position_temp + separation*speed_temp >
	   next_ptr->position - next_ptr->length)
	{
	  // must slow down to keep distance from vehicle in front
	  if (speed > 0.0F)     // can not back up
	  {
	    speed_temp = speed - decell*delta_time;
	    if (speed_temp < 0.0F)
	    {
	      speed_temp = 0.0F;
	    }
	  }
	  position_temp = position + speed_temp*delta_time;
	}
      }
    }
    position_temp = position + speed_temp*delta_time;

    // if signal is red, yellow or stop  in range, must decelerate
    switch (signal)
    {
      case red:
      case yellow:
      case stop:
      if (speed_temp >= speed)
      {
	stopping_distance = 0.5F * speed_temp * speed_temp / decell;
	if (stopping_distance > signal_pos-position_temp)
	{
	  if (speed > 0.0F)      // can not back up
	  {
	    speed_temp = speed - decell*delta_time;
	    if (speed_temp < 0.0F)
	    {
	      speed_temp = 0.0F;
	    }
	  }
	  position_temp = position + speed_temp*delta_time;
	}
      }
    }

    position = position_temp;
    speed  = speed_temp;
    draw();
} // end move

vehicle *vehicle::next(void)
{
    return next_ptr;
}

void vehicle::set_signal(signal_state Asignal)
{
    signal = Asignal;
}

void vehicle::draw(void)
{
    float x_offset;
    float y_offset;
    char any_string[4];
    sprintf(any_string,"%d",id);

    get_offsets(moving, x_offset, y_offset);
    if (position_last < signal_pos)
    {
      WRITE_TEXT(x_base+x_offset*position_last/scale,
		 y_base+y_offset*position_last/scale,
		 any_string,
		 CLEAR_FILL);
    }
    if (position < signal_pos)
    {
      WRITE_TEXT(x_base+x_offset*position/scale,
		 y_base+y_offset*position/scale,
		 any_string,
                 OUTLINE);
    }
} //  end draw
// end vehicle.cpp

void hw7vc(CDC *pDC)
{
  int i;

  vehicle *vehicle_list = NULL;

  printf("hw7 running \n"); // debug to a terminal window, if any
  delay(1);

  initialize(pDC);
  scale  = 50.0F;

  vehicle_list = new vehicle(16.0F, 3000.0F,
		    8.0F, 9.0F,
		    0.0F, 0.0F,
		    88.0F,
		    -38.0F, 0.0F,
		    east,
		    75.0F*scale,
		    vehicle_list);
  vehicle_list->set_signal(stop);

  for( i=0; i<4; i++)
  {
    delay(1);
    vehicle_list->move();
    PRINT();
  }

  vehicle_list = new vehicle(
		    16.0F, 3000.0F,
		    7.0F, 9.0F,
		    0.0F, 0.0F,
		    88.0F,
		    -38.0F, 0.0F,
		    east,
		    75.0F*scale,
		    vehicle_list);
  for( i=0; i<4; i++)
  {
    delay(1);
    (vehicle_list->next())->move();
    vehicle_list->move();
    PRINT();
  }

  vehicle_list = new vehicle(16.0F, 3000.0F,
			     6.0F, 9.0F,
			     0.0F, 0.0F,
			     88.0F,
			     -38.0F, 0.0F,
			     east,
			     75.0F*scale,
			     vehicle_list);
  for( i=1; i<30; i++)
  {
    delay(1);

// Homework 7 use 'set_new_lane_data(0.0, 88.0, 0.0, 0.0, north, 75.0*scale)'
// on one vehicle when i==10

    ((vehicle_list->next())->next())->move();
    (vehicle_list->next())->move();
    vehicle_list->move();
    PRINT();
  }

  CLOSE();
  printf("hw7 finished \n");
} // end hw7vc.cpp  main for homework

