/***************************************************************************

    file                 : pit.cpp
    created              : Thu Mai 15 2:43:00 CET 2003
    copyright            : (C) 2003 by Bernhard Wymann
    email                : berniw@bluewin.ch

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include "pit.h"

const float Pit::SPEED_LIMIT_MARGIN = 0.5;      /* [m/s] savety margin */
const int Pit::MIN_DAMAGE = 6000;              /* [-] */

Pit::Pit(tSituation *s, Driver *driver)
{
    track = driver->getTrackPtr();
    car = driver->getCarPtr();
    mypit = driver->getCarPtr()->_pit;
    pitinfo = &track->pits;
    pitstop = inpitlane = false;
    fuelchecked = false;
    fuelperlap = driver->fuelperlap;
    stops = driver->stops;
    lastpitfuel = 0.0;
    lastfuel = car->priv.fuel;
    

    if (mypit != NULL)
      {
      speedlimit = pitinfo->speedLimit - SPEED_LIMIT_MARGIN;
      speedlimitsqr = speedlimit*speedlimit;
      /* compute pit spline points along the track */
      p[3].x = mypit->pos.seg->lgfromstart + mypit->pos.toStart;
      p[2].x = p[3].x - pitinfo->len;
      p[4].x = p[3].x + pitinfo->len;
      p[0].x = pitinfo->pitEntry->lgfromstart;
      p[1].x = pitinfo->pitStart->lgfromstart;
      p[5].x = p[3].x + (pitinfo->nMaxPits - car->index)*pitinfo->len;
      p[6].x = pitinfo->pitExit->lgfromstart;
      pitentry = p[0].x;
      pitexit = p[6].x;
      
      /* normalizing spline segments to <= 0.0 */
      int i;
      for (i = 0; i < NPOINTS; i++)
	{
	p[i].s = 0.0;
	p[i].x = toSplineCoord(p[i].x);
        }

      if (p[1].x > p[2].x)
	p[1].x = p[2].x;
      if (p[4].x > p[5].x)
	p[5].x = p[4].x;

      float sign = (pitinfo->side == TR_LFT) ? 1.0 : -1.0;
      p[0].y = 0.0;
      p[6].y = 0.0;
      for (i = 1; i < NPOINTS - 1; i++)
	{
	p[i].y = fabs(pitinfo->driversPits->pos.toMiddle) - pitinfo->width;
	p[i].y *= sign;
        }
      p[3].y = fabs(pitinfo->driversPits->pos.toMiddle)*sign;
      spline = new Spline(NPOINTS, p);
      }
}

Pit::~Pit()
{
    if (mypit != NULL)
      delete spline;
}

/* Transforms track coordinates to spline parameter coordinates */
float Pit::toSplineCoord(float x)
{
    x -= pitentry;
    while (x < 0.0)
      {
      x += track->length;
      }
    return x;
}

/* computes offset to track middle for trajectory */
float Pit::getPitOffset(float offset, float fromstart)
{
    if (mypit != NULL)
      {
      if (getInPit() || (getPitstop() && isBetween(fromstart)))
	{
	fromstart = toSplineCoord(fromstart);
	return spline->evaluate(fromstart);
        }
      }
    return offset;
}

/* Sets the pitstop flag if we are not in the pit range */
void Pit::setPitstop(bool pitstop)
{
    if (mypit == NULL)
      return;
    float fromstart = car->_distFromStartLine;

    if (!isBetween(fromstart))
      {
      this->pitstop = pitstop;
      }
    else if (!pitstop)
      {
      this->pitstop = pitstop;
      }
}

/* Check if the argument fromstart is in the range of the pit */
bool Pit::isBetween(float fromstart)
{
    if (pitentry <= pitexit)
      {
      if (fromstart >= pitentry && fromstart <= pitexit)
	{
	  return true;
        }
      else
	{
	return false;
        }
      }
    else
      {
      if ((fromstart >= 0.0 && fromstart <= pitexit) ||
            (fromstart >= pitentry && fromstart <= track->length))
        {
	return true;
        }
      else
	{
	return false;
        }
      }
}

/* update pit data and strategy */
void Pit::update()
{
    /* Don't pit if there's no pit or we're on the last lap */
    if (mypit != NULL && car->_remainingLaps != 0)
      {
      if (isBetween(car->_distFromStartLine))
	{
        if (getPitstop())
	  {
	  setInPit(true);
	  }
        }
      else
	{
        setInPit(false);
        }
      /* check for damage */
      //if (car->_dammage > PIT_DAMMAGE)
      int laps = car->_remainingLaps-car->_lapsBehindLeader;
      float critical_damage = (10000 / (car->_laps + car->_remainingLaps)) * car->_remainingLaps;
      if(car->_dammage > MIN_DAMAGE && car->_dammage > 10000 - critical_damage)
	{
        setPitstop(true);
        }
      /* fuel update */
      int id = car->_trkPos.seg->id;
      if (id >= 0 && id < 5 && !fuelchecked)
	{
	fuelchecked = true;
        }
      else if (id > 5)
	{
        fuelchecked = false;
        }
      //int laps = car->_remainingLaps-car->_lapsBehindLeader;
      if (!getPitstop() && laps > 0)
	{
        if (car->_fuel < 1.5*fuelperlap && car->_fuel < laps*fuelperlap)
	  {
          setPitstop(true);
	  }
        }
      if (getPitstop())
	car->_raceCmd = RM_CMD_PIT_ASKED;
      }
}

/* Computes the amount of fuel */
float Pit::getFuel()
{
    /* Work out how much fuel is needed for the rest of the race */
    float fuelforrace = fuelperlap * (car->_laps + car->_remainingLaps + 1.0);
    float fuelforrestofrace = fuelperlap * (car->_remainingLaps + 1.0);

    /* We need to refuel */
    if(fuelforrestofrace >= car->_fuel)
      {
      /* fuel to the finish if we can */
      if(fuelforrestofrace <= 100.0)
	return fuelforrestofrace - car->_fuel;
      /* We'll need to stop again after this, fuel for the next stint */
      return (fuelforrace / stops) - car->_fuel;
      }

    /* No fuel needed */
    return 0.0;
}

/* Computes how much damage to repair */
int Pit::getRepair()
{
  /* Repair a factor of laps remaining */
  int to_repair = (car->_dammage / (car->_laps + car->_remainingLaps)) * car->_remainingLaps;
  while(car->_dammage - to_repair > MIN_DAMAGE)
    {
    to_repair *= (car->_dammage / MIN_DAMAGE);
    }
  return to_repair;
}

