/***************************************************************************
 *   Copyright (C) 2005 by Brian Lauber   *
 *   bml8@case.edu   *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <sys/shm.h>

#include "blError.h"
#include "blRandom.h"
#include "shared.h"
#include "semaphores.h"


int main(int argc, char *argv[])
{
  /********************************************
                    VARIABLES
  ********************************************/
  key_t              S_Grp;
  key_t              M_Grp;

  struct SharedVars *shared;

  int               delay = 0;
  /********************************************
                     PROGRAM
  ********************************************/
  // Step 1: Discover the shared memory and semaphore group keys
  //         from the environment variables
  S_Grp = GetS_Group();
  M_Grp = GetM_Group();

  // Step 2: Attach the shared memory to the process
  Error(shared = shmat(M_Grp, 0, 0));

  // Step 3: Attach to the randomizer process and sleep for 1 to 60 seconds
  AttachRandomizer();
  printf("Train will sleep %2i seconds\n", (delay = 1 + (blRand() % 60)));
  sleep(delay);

  // Step 4: Approach the crossing
  Wait(S_Grp, CROSSING);
  // If the light is GREEN (cars are coming), warn them by changing the light
  if(shared->Light == GREEN)
  {
    if(shared->CarsCrossing > 0)
      shared->Light = YELLOW;
    else
      shared->Light = RED;
  }
  shared->TrainsComing++;

  // Trains cannot cross until: 1, The light is RED
  //                            2, The intersection is empty (of both trains and cars)
  if( (shared->Light != RED) || (shared->TrainsCrossing > 0) )
  {
    PrintStatus("Train waits", shared);
    Signal(S_Grp, CROSSING); // Allow other processes to check the CROSSING
    Wait(S_Grp, TRAIN);      // but this process will wait on TRAIN
  }

  // Now the train is about to enter the crossing.
  // Reduce the TrainsComing count and increase TrainsCrossing
  shared->TrainsComing--;
  shared->TrainsCrossing++;
  PrintStatus("Train enters", shared);
  Signal(S_Grp, CROSSING);

  sleep(1);

  Wait(S_Grp, CROSSING);
  shared->TrainsCrossing--; // Train exits crossing -- reduce the count
  PrintStatus("Train exits", shared);

  // Next is the tricky part.  Depending upon the state of the system, we need to signal a different
  // semaphore.  If there are more trains waiting to cross, then we need to signal TRAIN to let the
  // train through.  If no trains are waiting but there are cars waiting, then we need to signal CAR
  // to let the waiting cars go first (otherwise, cars will keep waiting at the light until new cars
  // arrive).  Lastly, if no one is waiting, then we can just signal CROSSING to let anyone through.
  if(shared->TrainsComing == 0)
  {
    shared->Light = GREEN;
    if(shared->CarsWaiting > 0)
      Signal(S_Grp, CAR);
    else
      Signal(S_Grp, CROSSING);
  }
  else
    Signal(S_Grp, TRAIN);

  return EXIT_SUCCESS;
}


