/***************************************************************************
 *   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 <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

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


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


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

  struct SharedVars *shared = NULL; // The shared memory will be overlayed w/ this structure

  // Initially, the crossing can contain 4 cars
  // or 1 train.  Therefore, we're guessing that
  // we need to set the following semaphore values
  unsigned short SemLimits[] = {0, 0, 1};
  union    semun UnionThing; // Needed to pass SemLimits to semctl()

  size_t         i = 0;      // Used for looping
  /********************************************
                    PROGRAM
  ********************************************/
  // Step 1: Create the shared resources.
  S_Grp = Error(semget(IPC_PRIVATE, 3, IPC_CREAT | 0666));
  M_Grp = Error(shmget(IPC_PRIVATE, sizeof(struct SharedVars), IPC_CREAT | 0666));

  // Step 2: Initialize the semaphore limits
  UnionThing.array = SemLimits;
  Error(semctl(S_Grp, 0, SETALL, UnionThing) );

  // Step 3: Map the shared memory to the current process (before this point, the shared
  //         memory existed somewhere in the operating system; the memory must be attached
  //         to the current process before it can be accessed)
  Error(shared = shmat(M_Grp, 0, 0));

  // Step 4: Initialize the shared memory
  shared->CarsWaiting = shared->TrainsCrossing = 0;
  shared->Light = GREEN;


  // Step 5: Export the semaphore and shared memory group id's so other processes can
  //         use the shared resources
  SetS_Group(S_Grp);
  SetM_Group(M_Grp);

  // Step five and one half: Create the random number generator process
  AttachRandomizer(); // I recommend you just use srand() and rand() to generate random numbers

  // Step 6: Create the other processes
  for(i = 0; i < 60; ++i)
    if(Error(vfork()) == 0)
  Error(execl("car", "car", NULL));
  for(i = 0; i < 10; ++i)
    if(Error(vfork()) == 0)
      Error(execl("train", "train", NULL));

  // Step 7: Wait for all the child processes to exit
  for(i = 0; i < 70; ++i)
    Error(wait(NULL));

  printf("                    CW    CC    TW    TC      Light\n");
  printf("CW: Cars   waiting\n");
  printf("CC: Cars   crossing\n");
  printf("TW: Trains waiting\n");
  printf("TC: Trains crossing\n");

  // Step 8: Detach the shared memory
  Error(shmdt(shared));

  // Step 9: Delete the shared resources
  Error(semctl(S_Grp, 0, IPC_RMID));
  Error(shmctl(M_Grp, IPC_RMID, NULL));
  return EXIT_SUCCESS;
}
