/* train.h
 *
 * The train class.  A train does two principal things: it transports
 * passengers and it advances along the track.
 * 
 * To transport passengers: When the train arrives at a platform, it
 * moves into the platform's station each passenger that wants to get
 * off at that station.  Then it signals the platform of its arrival
 * and the platform transfers passengers into the train.
 *
 * To advance along the track: Each time the train advances along the
 * track, it looks at the status of the next section of track and
 * adjusts its speed accordingly.  Upon arriving in a platform, it
 * slows down, stops, and transfers some passengers.  Upon leaving a
 * platform, it looks up the next station on its path, informs the
 * controller of this next destination, and heads in that direction.
 * If this requires that the train reverse, it informs the controller
 * that it is reversing.
 *
 * Each section of track is directed, and the train records which
 * sections of track it is on, as well as the position on each of
 * those sections.
 */


#ifndef TRAIN_H
#define TRAIN_H

#include "defs.h"
#include "seq.h"
#include "main.h"
#include "section.h"
#include "station.h"

class Passenger;


#define LOADING_TIME                8    // 8 seconds fixed cost per stop
#define LOADING_TIME_PER_PASSENGER  0.04 // 0.04 seconds per passenger
#define REVERSING_TIME              30   // 30 seconds to reverse the train

#define TRAIN_CAPACITY             400   // one train can carry 400 persons
#define TRAIN_LENGTH               150   // train is 150 metres long
#define CARS_PER_TRAIN               6

#define MAX_TRAIN_ACCEL             0.1   // maximum acceleration (m/s/s)
#define MIN_TRAIN_ACCEL            -1.8   // maximum acceleration (m/s/s)


// one element on a train's path

typedef enum { STATION, REVERSE, BRANCH } PathElementType;

class PathElement {
public:

  PathElementType type;
  Station *station;
  Section *branch;

  PathElement( Station *s ) {
    type = STATION;
    station = s;
  }

  PathElement( Section *b ) {
    type = BRANCH;
    branch = b;
  }

  PathElement() {
    type = REVERSE;
  }

  friend ostream& operator << ( ostream& stream, PathElement const &pe ) {
    switch (pe.type) {
    case STATION: stream << *pe.station; break;
    case BRANCH:  stream << *pe.branch; break;
    case REVERSE: stream << "reverse"; break;
    }
    return stream;
  }

};



class Train {

  friend void setupNetwork(char*c);
  friend class Statistics;

  static int nextIdent;		// for generating train idents

  int ident;			// unique train identifier
  float length;			// length of train in metres

  seq<Passenger*> passengers;	// passenger list

  seq<PathElement> path;	// stations visited by the train
  int nextStationIndex;		// next station in path

  float lastUpdateTime;		// last time train's position was updated
  float speed;			// current speed of train (m/s)
  float acceleration;		// current acceleration of train (m/s/s)

  PlatformStatus platformStatus; // status when in a platform section

  seq<Section*> sections;	// sections currently occupied by train
  float frontDistRemaining;	// distance remaining in front section
  float rearDistRemaining;	// distance remaining in rear section

  // Some functions used internally.  See comments in train.C

  float calcNewAcceleration();
  float computeNecessaryAccel( float currentSpeed, float desiredSpeed, float distance );
  void updatePositionInSections( float positionChange );
  Section *nextSection( Section *s );
  int disembarkPassengers( Station *s );
  void reverseDirection();

public:

  // Constructor

  Train( Section *s ) {
    ident = nextIdent++;
    length = TRAIN_LENGTH;
    nextStationIndex = 0;
    lastUpdateTime = currentTime;
    speed = 0;
    acceleration = 0;
    platformStatus = LEAVING;
    sections.add( s );
    frontDistRemaining = (s->length() - length) / 2.0;
    rearDistRemaining = frontDistRemaining + length;
    s->setOccupied( true );
  }

  // Signal the train to advance for a certain amount of time.  The
  // train calculates its new position and accelerates or
  // decellerates, depending on the status of the next track section
  // and whether it is arriving at or leaving a platform.

  void advance();

  // Return true if the train will be going to a particular station, s.

  bool isStoppingAtStation( Station *s );

  // Add a passenger to the train

  void addPassenger( Passenger *p );

  // Signal the train to depart from the platform

  void departFromPlatform();

  // Return true if there's space left in the train

  bool spaceLeft() {
    return passengers.size() < TRAIN_CAPACITY;
  }

  // Return the remaining space on the train

  int remainingSpace() {
    return TRAIN_CAPACITY - passengers.size();
  }

  // Return current section

  Section *currentSection() {
    return sections[ sections.size()-1 ];
  }

  // Output operators

  void draw();

  void printPassengers();

  friend ostream& operator << ( ostream& stream, Train const &t ) {
    stream << "T" << t.ident;
    return stream;
  }
};

#endif
