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

#include <pthread.h>

#include "specs.h"

/* Semaphore-related */
#include <semaphore.h>

#define LOCAL_TO_CURRENT_THREAD 0

sem_t display_mutex;


/* Global variables that hold the "positions" on the bridge in each
   direction. */
int right[POSN_EACH_DIRECTION];
int left[POSN_EACH_DIRECTION];

#define TRUE 1
#define FALSE (! TRUE)

int done = FALSE;
int num_cars;
int cars_out_right = 0;
int cars_out_left  = 0;

void my_sleep( ) {
  usleep( 100000 );
}

void *enter_right( void *ptr ) {
  int i;
  for( i = 0; i < num_cars; ++i ) {
    int car = floor( drand48() * MAX_COLOURS );
    right[0] = car;
    sleep( 1 );
  }
  pthread_exit( NULL );
}

void *leave_right( void *ptr ) {
  while( ! done ) {
    if( right[POSN_EACH_DIRECTION-1] != white ) {
      right[POSN_EACH_DIRECTION-1] = white;
      cars_out_right = cars_out_right + 1;
    }
    
    sem_wait( &display_mutex );
    update_display( right, left );
    sem_post( &display_mutex );

    my_sleep();
  }
  pthread_exit( NULL );
}


void *enter_left( void *ptr ) {
  int i;
  for( i = 0; i < num_cars; ++i ) {
    int car = floor( drand48() * MAX_COLOURS );
    left[POSN_EACH_DIRECTION-1] = car;
    sleep( 1 );
  }
  pthread_exit( NULL );
}

void *leave_left( void *ptr ) {
  while( ! done ) {
    if( left[0] != white ) {
      left[0] = white;
      cars_out_left = cars_out_left + 1;
    }

    sem_wait( &display_mutex );
    update_display( right, left );
    sem_post( &display_mutex );

    my_sleep();
  }
  pthread_exit( NULL );
}


void *move( void *ptr ) {
  int i;
  while( ! done ) {
    for( i = POSN_EACH_DIRECTION - 2; i >= 0; i-- ) {
      if( right[i + 1] == white && right[i] != white ) {
	right[i + 1] = right[i];
	right[i]     = white;

	sem_wait( &display_mutex );
	update_display( right, left );
	sem_post( &display_mutex );

	my_sleep();
      }
    }
    for( i = 1; i < POSN_EACH_DIRECTION; ++i ) {
      if( left[i - 1] == white && left[i] != white ) {
	left[i - 1] = left[i];
	left[i]     = white;

	sem_wait( &display_mutex );
	update_display( right, left );
	sem_post( &display_mutex );

	my_sleep();
      }
    }      
  }
  pthread_exit( NULL );
}


int main( int argc, char* argv[] ) {
  int i;
  pthread_t move_thread, leaver_thread, leavel_thread;
  pthread_t enterr_thread, enterl_thread;

  sem_init( &display_mutex, LOCAL_TO_CURRENT_THREAD, 1 );

  if( argc != 2 ) {
    fprintf( stderr, "Usage: ./bridge <number of cars>\n" );
    exit( 1 );
  }
  num_cars = atoi( argv[1] );

  srand48( 145789 );

  for( i = 0; i < POSN_EACH_DIRECTION; ++i ) {
    right[i] = white;
    left[i]  = white;
  }

  initialise_display();

  update_display( right, left );
  my_sleep();

  pthread_create( &move_thread, NULL, move, NULL );
  pthread_create( &leaver_thread, NULL, leave_right, NULL );
  pthread_create( &leavel_thread, NULL, leave_left, NULL );
  pthread_create( &enterr_thread, NULL, enter_right, NULL );
  pthread_create( &enterl_thread, NULL, enter_left, NULL );

  pthread_join( enterr_thread, NULL );
  pthread_join( enterl_thread, NULL );

  while( num_cars != cars_out_right ) {
    printf( "Right: %d cars entered, %d left\n", num_cars, cars_out_right );
    sleep( 1 );
  }
  printf( "Right: %d cars entered, %d left\n", num_cars, cars_out_right );
  while( num_cars != cars_out_left ) {
    printf( "Left: %d cars entered, %d left\n", num_cars, cars_out_left );
    sleep( 1 );
  }
  printf( "Left: %d cars entered, %d left\n", num_cars, cars_out_left );
  done = TRUE;

  close_display();

  return 0;
}


