// File: RaceCourse.cpp
//
// Written by Richard Chang for CMSC202 Spring 2007
//

#include <iostream>
#include <cassert>
//#include "Car.h"
#include "RaceCourse.h"

using namespace std ;

// ----------------------------------------------------
// --------------------- Local Types ------------------
//

// THINGs in the RaceCourse that are not Cars.
//
enum THING {
   WALL = -1,
   SPACE = 0  
} ;


// ----------------------------------------------------
// --------------------- Constructors -----------------
//

RaceCourse::RaceCourse() {
   // do nothing
   Status = WAITING ;
}

RaceCourse::RaceCourse(unsigned int r, unsigned int c, 
  unsigned int n, const vector<Position>& obs) {

  rows = r ;    // could have used member initializers
  cols = c ;
  cars = n ;
  Status = WAITING ;

  // Make walls around the perimeter
  for (unsigned int i=0 ; i <= cols+1 ; i++) {
      Course[0][i] = WALL ;
      Course[rows+1][i] = WALL ;
  }

  for (unsigned int i=0 ; i <= rows+1 ; i++) {
      Course[i][0] = WALL ;
      Course[i][cols+1] = WALL ;
  }

  // Fill race course with spaces
  for (unsigned int i=1 ; i<=rows ; i++) {
     for (unsigned int j = 1 ; j<=cols ;j++) {
        Course[i][j] = SPACE ;
     }
  }


  // Initialize each car

  for (unsigned int i = 1 ; i <= cars ; i++) {
     CarList[i] = Car(this, i, InitialFuelLevel) ;
     CarPos[i].row = 1 ;
     CarPos[i].col = i ;
     Course[1][i] = i ;
  }


  // Install obstacles
  unsigned int x, y ;

  for (unsigned int i=0 ; i < obs.size() ; i++) {
      x = obs[i].row ;    
      y = obs[i].col ;

      // Check that there's nothing there
      assert((1 <= x) && (x <= rows)) ;
      assert((1 <= y) && (y <= cols)) ;
      assert(Course[x][y] == SPACE) ;

      Course[x][y] = WALL ;
  }
}


Car * RaceCourse::GetCar(unsigned int i) {
   assert( (1 <= i) && (i <= cars) ) ;

   return &(CarList[i]) ;
}


unsigned int RaceCourse::GetNumOfCars() const {
   return cars ;
}


unsigned int RaceCourse::GetNumOfRows() const {
   return rows ;
}


unsigned int RaceCourse::GetNumOfCols() const {
   return cols ;
}


void RaceCourse::StartRace() {
   Status = STARTED ;
   Winner = 0 ;
}


RaceStatus RaceCourse::GetRaceStatus() {

   if (Status != STARTED) return Status ;

   for (unsigned int i=1 ; i <= cars ; i++) {
      if (CarList[i].GetFuelLevel() > 0) return STARTED ;
   }

   return Status = STALEMATE ;
}


// Used by PrettyPrintCourse()
//
void RaceCourse::PrintRaceStatus() {
   RaceStatus s ;

   s = GetRaceStatus() ;
   switch(s) {
   case WAITING : cout << "Status: WAITING\n" ; break ;
   case STARTED : cout << "Status: STARTED\n" ; break ;
   case FINISHED : cout << "Status: FINISHED\n" ; break ;
   case STALEMATE : cout << "Status: STALEMATE\n" ; break ;
   default: cout << "Status: UNKNOWN (Bad)\n" ;
   }
}


unsigned int RaceCourse::GetWinner() const {
   return Winner ;
}


void RaceCourse::PrettyPrintCourse() {

unsigned int i, j ;
int thing ;

   for (i = 0 ; i <= rows+1 ; i++) {
      for (j = 0 ; j <= cols+1 ; j++) {
         thing = Course[i][j] ;

         if (thing == WALL) {
            cout << "#" ;
         } else if (thing == SPACE) {
            cout << ".";
         } else {
            cout << thing ;
         }
      }
      cout << endl ;
   }
   PrintRaceStatus() ;
}


// facilitator for Car objects to call.
// max = maximum number of steps to move.
// xinc = -1, 0, +1 change in rows
// yinc = -1, 0, +1 change in cols
// Setting xinc and yinc allows Cars to specify
// the direction (left, right, up or down) that they want to move.
//

unsigned int RaceCourse::MoveMe(
     unsigned int CarIndex, unsigned int max, int xinc, int yinc) 
{
   unsigned int steps ;
   unsigned int x, y ;
   RaceStatus s ;

   assert((1 <= CarIndex) && (CarIndex <= cars)) ;   
   assert((xinc == 0) || (yinc == 0)) ;
   assert((-1 <= xinc) && (xinc <= 1)) ;
   assert((-1 <= yinc) && (yinc <= 1)) ;

   s = GetRaceStatus() ;  // actually computes STALEMATE possibility
   if (s != STARTED) return 0 ;

   steps = 0 ;
   x = CarPos[CarIndex].row ;   // OK, maybe x should be the col number
   y = CarPos[CarIndex].col ;
   
   // cout << "Debug: (x,y) = (" << x << "," << y << ")\n" ;

   Course[x][y] = SPACE ;

   // Keep going in the specified direction until
   // we run out of gas or hit something.
   //
   while ((steps < max) && (Course[x+xinc][y+yinc] == SPACE)) {
      steps++ ;
      x += xinc ;
      y += yinc ;
      // cout << "Debug: (x,y) = (" << x << "," << y << ")\n" ;
   }
   

   // Update Car's position.
   //
   CarPos[CarIndex].row = x ;
   CarPos[CarIndex].col = y ;

   Course[x][y] = CarIndex ;

   if ((x == rows) && (y == 1)) {
      Status = FINISHED ;
      Winner = CarIndex ;
   }

   return steps ;
}


// Returns the Position of the Car specified by CarIndex
//
Position RaceCourse::GetMyPosition(unsigned int CarIndex) const {
   
   assert((1 <= CarIndex) && (CarIndex <= cars)) ;   

   return CarPos[CarIndex] ;
}
