/****************************************************************/
/* op.c                                                         */
/* Justin Hartman                                               */
/* EECS 338 - Spring 2003                                       */
/* Assignment 5                                                 */
/****************************************************************/

/****************************************************************/
/* Written in Microsoft Visual C++ .NET                         */
/* Compiled and executed on CWRU's EECS department              */
/* lab's (Olin 404.5) SUN boxes using SSH and                   */
/* secure FTP                                                   */
/****************************************************************/

#include "sem.h"
#include "queue.h"

int main(int argc, char *argv[]) {
	int shmid, semid_lines, mutex_q, mutex_count;
	int i;
	union semun semctlarg;
	int pid = (int)getpid();
	short seminit[4];
	
	char timestr[17];
	int *skicount;

	bool loadingls = true;
	bool loadinglt = true;
	bool ltloaded;
	bool rtloaded;
	bool lsloaded;
	bool rsloaded;

	int lscount;
	int ltcount;
	int rtcount;
	int rscount;

	short skiersloaded;

	queue *q[4];

	/* Seed the random number generator */
	srand((int)time(0));

	/* Initialize the shared queues */
	for (i=0; i<4; i++) {
		if ((shmid=shmget(pid + i, sizeof(queue), 0700|IPC_CREAT)) == -1) {
			perror("op shmget of queues failed");
			exit(1);
		}
		if ((q[i]=(queue *)shmat(shmid,0,0)) == (queue *)-1) {
			perror("op shmat attach of queues failed");
			exit(2);
		}
		initialize (q[i]);
	}

	/* Initialize the shared skier count */
	if ((shmid=shmget(pid + 4, sizeof(int), 0700|IPC_CREAT)) == -1) {
		perror("op shmget of skier count failed");
		exit(3);
	}
	if ((skicount=(int *)shmat(shmid,0,0)) == (int *)-1) {
		perror("op shmat attach of skier count failed");
		exit(4);
	}
	(*skicount)=0;

	/* Set up the four line semaphores */
	if ((semid_lines=semget(pid + 5, 4, 0700|IPC_CREAT)) == -1) {
		perror("op semget of line semaphores failed");
		exit(5);
	}
    seminit[0]=1;	/* Left single */
    seminit[1]=3;	/* Left triple */
	seminit[2]=3;	/* Right triple */
	seminit[3]=1;	/* Right single */
    semctlarg.array = seminit;
	if (semctl(semid_lines,4,SETALL,semctlarg) == -1) {
		perror("op semctl SETALL of line semaphores failed");
		exit(6);
	}

	/* Set up the queue mutex semaphore */
	if ((mutex_q=semget(pid + 6, 1, 0700|IPC_CREAT)) == -1) {
		perror("op semget of queue mutex semaphore failed");
		exit(7);
	}
	seminit[0]=1;
	semctlarg.array = seminit;
	if (semctl(mutex_q,1,SETALL,semctlarg) == -1) {
		perror("op semctl SETALL of skier count mutex semaphore failed");
		exit(8);
	}

	/* Set up the skier count mutex semaphore */
	if ((mutex_count=semget(pid + 7, 1, 0700|IPC_CREAT)) == -1) {
		perror("op semget of skier count mutex semaphore failed");
		exit(9);
	}
	seminit[0]=1;
	semctlarg.array = seminit;
	if (semctl(mutex_count,1,SETALL,semctlarg) == -1) {
		perror("op semctl SETALL of skier count mutex semaphore failed");
		exit(10);
	}

	/* Fork the skier process, which will itself fork all the skiers */
	if (fork()==0) {
		execl("skier","skier",0);
		exit(0);
	}

	sleep(5); /* Wait for skiers to get initialized */

	P(mutex_count, 0);
	while ((*skicount)>0) {
		V(mutex_count, 0);
		//Check the lines
		//Remove the appropriate skiers and print PIDs and times

		P(mutex_q, 0);

		lscount = count(q[0]);
		ltcount = count(q[1]);
		rtcount = count(q[2]);
		rscount = count(q[3]);

		if (ltcount >= 3) ltloaded = true;
		else ltloaded = false;

		if (rtcount >= 3) rtloaded = true;
		else rtloaded = false;

		if (lscount >= 1) lsloaded = true;
		else lsloaded = false;

		if (rscount >= 1) rsloaded = true;
		else rsloaded = false;

		if (loadinglt) {
			// Left triple has priority
			if (ltloaded) {
				//Left triple is loaded
				if (loadingls) {
					//left single has priority
					if (lsloaded) {
						//Left single is loaded
						// Load left triple and left single
						mktimestr(timestr);
						pid = removeq(q[0]);
						printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
						for (i=0; i<3; i++) {
							pid = removeq(q[1]);
							printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
						}

						V(semid_lines, 0);
						for (i=0; i<3; i++) V(semid_lines, 1);

						loadinglt = false;
						loadingls = false;
						V(mutex_q, 0);
					} else {
						//Left single is not loaded
						if (rsloaded) {
							//Right single is loaded
							// Load left triple and right single
							mktimestr(timestr);
							pid = removeq(q[3]);
							printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[1]);
								printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 3);
							for (i=0; i<3; i++) V(semid_lines, 1);

							loadinglt = false;
							loadingls = true;
							V(mutex_q, 0);
						} else { //No singles loaded
							// Load just the left triple
							mktimestr(timestr);
							printf("\nOperator: No single lines are loaded.");
							for (i=0; i<3; i++) {
								pid = removeq(q[1]);
								printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							for (i=0; i<3; i++) V(semid_lines, 1);

							loadinglt = false;
							V(mutex_q, 0);
						}
					}
				} else {
					//right single has priority
					if (rsloaded) {
						//Right single is loaded
						// Load left triple and right single
						mktimestr(timestr);
						pid = removeq(q[3]);
						printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
						for (i=0; i<3; i++) {
							pid = removeq(q[1]);
							printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
						}

						V(semid_lines, 3);
						for (i=0; i<3; i++) V(semid_lines, 1);

						loadinglt = false;
						loadingls = true;
						V(mutex_q, 0);
					} else {
						//Right single is not loaded
						if (lsloaded) {
							//Left single is loaded
							// Load left triple and left single
							mktimestr(timestr);
							pid = removeq(q[0]);
							printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[1]);
								printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 0);
							for (i=0; i<3; i++) V(semid_lines, 1);

							loadinglt = false;
							loadingls = false;
							V(mutex_q, 0);
						} else { //No singles loaded
							// Load just the left triple
							mktimestr(timestr);
							printf("\nOperator: No single lines are loaded.");
							for (i=0; i<3; i++) {
								pid = removeq(q[1]);
								printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							for (i=0; i<3; i++) V(semid_lines, 1);

							loadinglt = false;
							V(mutex_q, 0);
						}
					}
				}
			} else {
				//Left triple is not loaded
				if (rtloaded) {
					//Right triple is loaded
					if (loadingls) {
						//left single has priority
						if (lsloaded) {
							//Left single is loaded
							// Load right triple and left single
							mktimestr(timestr);
							pid = removeq(q[0]);
							printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[2]);
								printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 0);
							for (i=0; i<3; i++) V(semid_lines, 2);

							loadinglt = true;
							loadingls = false;
							V(mutex_q, 0);
						} else {
							//Left single is not loaded
							if (rsloaded) {
								// Right single is loaded
								// Load right triple and right single
								mktimestr(timestr);
								pid = removeq(q[3]);
								printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
								for (i=0; i<3; i++) {
									pid = removeq(q[2]);
									printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								V(semid_lines, 3);
								for (i=0; i<3; i++) V(semid_lines, 2);

								loadinglt = true;
								loadingls = true;
								V(mutex_q, 0);
							} else { //No singles loaded
								// Load just the right triple
								mktimestr(timestr);
								printf("\nOperator: No single lines are loaded.");
								for (i=0; i<3; i++) {
									pid = removeq(q[2]);
									printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								for (i=0; i<3; i++) V(semid_lines, 2);

								loadinglt = true;
								V(mutex_q, 0);
							}
						}
					} else {
						//right single has priority
						if (rsloaded) {
							//Right single is loaded
							// Load right triple and right single
							mktimestr(timestr);
							pid = removeq(q[3]);
							printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[2]);
								printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 3);
							for (i=0; i<3; i++) V(semid_lines, 2);

							loadinglt = true;
							loadingls = true;
							V(mutex_q, 0);
						} else {
							//Right single is not loaded
							if (lsloaded) {
								//Left single is loaded
								// Load right triple and left single
								mktimestr(timestr);
								pid = removeq(q[0]);
								printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
								for (i=0; i<3; i++) {
									pid = removeq(q[2]);
									printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								V(semid_lines, 0);
								for (i=0; i<3; i++) V(semid_lines, 2);

								loadinglt = true;
								loadingls = false;
								V(mutex_q, 0);
							} else { //No singles loaded
								// Load just the right triple
								mktimestr(timestr);
								printf("\nOperator: No single lines are loaded.");
								for (i=0; i<3; i++) {
									pid = removeq(q[2]);
									printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								for (i=0; i<3; i++) V(semid_lines, 2);

								loadinglt = true;
								V(mutex_q, 0);
							}
						}
					}
				} else { //No triples fully loaded
					skiersloaded=0;
					mktimestr(timestr);

					if ((loadinglt) && (ltcount > 0)) {
						for (i=0; i<ltcount; i++) {
							pid = removeq(q[1]);
							printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							V(semid_lines, 1);
							skiersloaded++;
						}
						loadinglt = false;
					} else if (rtcount > 0) {
						for (i=0; i<rtcount; i++) {
							pid = removeq(q[2]);
							printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							V(semid_lines, 2);
							skiersloaded++;
						}
						loadinglt = true;
					}
					
					if (skiersloaded<4) {
						if (lsloaded) {
							pid = removeq(q[0]);
							printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
							V(semid_lines, 0);
							skiersloaded++;
						}
					}

					if (skiersloaded<4) {
						if (rsloaded) {
							pid = removeq(q[3]);
							printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
							V(semid_lines, 3);
							skiersloaded++;
						}
					}

					if (skiersloaded<4) {
						printf("\nOperator: Unable to fully load lift, due to unfilled lines.");
					}

					V(mutex_q, 0);
				}
			}
		} else {
			// Right triple has priority
			if (rtloaded) {
				//Right triple is loaded
				if (loadingls) {
					//left single has priority
					if (lsloaded) {
						//Left single is loaded
						// Load right triple and left single
						mktimestr(timestr);
						pid = removeq(q[0]);
						printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
						for (i=0; i<3; i++) {
							pid = removeq(q[2]);
							printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
						}

						V(semid_lines, 0);
						for (i=0; i<3; i++) V(semid_lines, 2);

						loadinglt = true;
						loadingls = false;
						V(mutex_q, 0);
					} else {
						//Left single is not loaded
						if (rsloaded) {
							//Right single is loaded
							// Load right triple and right single
							mktimestr(timestr);
							pid = removeq(q[3]);
							printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[2]);
								printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 3);
							for (i=0; i<3; i++) V(semid_lines, 2);

							loadinglt = true;
							loadingls = true;
							V(mutex_q, 0);
						} else { //No singles loaded
							// Load just the right triple
							mktimestr(timestr);
							printf("\nOperator: No single lines are loaded.");
							for (i=0; i<3; i++) {
								pid = removeq(q[2]);
								printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							for (i=0; i<3; i++) V(semid_lines, 2);

							loadinglt = true;
							V(mutex_q, 0);
						}
					}
				} else {
					//right single has priority
					if (rsloaded) {
						//Right single is loaded
						// Load right triple and right single
						mktimestr(timestr);
						pid = removeq(q[3]);
						printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
						for (i=0; i<3; i++) {
							pid = removeq(q[2]);
							printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
						}

						V(semid_lines, 3);
						for (i=0; i<3; i++) V(semid_lines, 2);

						loadinglt = true;
						loadingls = true;
						V(mutex_q, 0);
					} else {
						//Right single is not loaded
						if (lsloaded) {
							//Left single is loaded
							// Load right triple and left single
							mktimestr(timestr);
							pid = removeq(q[0]);
							printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[2]);
								printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 0);
							for (i=0; i<3; i++) V(semid_lines, 2);

							loadinglt = true;
							loadingls = false;
							V(mutex_q, 0);
						} else { //No singles loaded
							// Load just the right triple
							mktimestr(timestr);
							printf("\nOperator: No single lines are loaded.");
							for (i=0; i<3; i++) {
								pid = removeq(q[2]);
								printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							for (i=0; i<3; i++) V(semid_lines, 2);

							loadinglt = true;
							V(mutex_q, 0);
						}
					}
				}
			} else {
				//Right triple is not loaded
				if (ltloaded) {
					//Left triple is loaded
					if (loadingls) {
						//left single has priority
						if (lsloaded) {
							//Left single is loaded
							// Load left triple and left single
							mktimestr(timestr);
							pid = removeq(q[0]);
							printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[1]);
								printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 0);
							for (i=0; i<3; i++) V(semid_lines, 1);

							loadinglt = false;
							loadingls = false;
							V(mutex_q, 0);
						} else {
							//Left single is not loaded
							if (rsloaded) {
								//Right single is loaded
								// Load left triple and right single
								mktimestr(timestr);
								pid = removeq(q[3]);
								printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
								for (i=0; i<3; i++) {
									pid = removeq(q[1]);
									printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								V(semid_lines, 3);
								for (i=0; i<3; i++) V(semid_lines, 1);

								loadinglt = false;
								loadingls = true;
								V(mutex_q, 0);
							} else { //No singles loaded
								// Load just the left triple
								mktimestr(timestr);
								printf("\nOperator: No single lines are loaded.");
								for (i=0; i<3; i++) {
									pid = removeq(q[1]);
									printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								for (i=0; i<3; i++) V(semid_lines, 1);

								loadinglt = false;
								V(mutex_q, 0);
							}
						}
					} else {
						//right single has priority
						if (rsloaded) {
							//Right single is loaded
							// Load left triple and right single
							mktimestr(timestr);
							pid = removeq(q[3]);
							printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
							for (i=0; i<3; i++) {
								pid = removeq(q[1]);
								printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							}

							V(semid_lines, 3);
							for (i=0; i<3; i++) V(semid_lines, 1);

							loadinglt = false;
							loadingls = true;
							V(mutex_q, 0);
						} else {
							//Right single is not loaded
							if (lsloaded) {
								//Left single is loaded
								// Load left triple and left single
								mktimestr(timestr);
								pid = removeq(q[0]);
								printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
								for (i=0; i<3; i++) {
									pid = removeq(q[1]);
									printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								V(semid_lines, 0);
								for (i=0; i<3; i++) V(semid_lines, 1);

								loadinglt = false;
								loadingls = false;
								V(mutex_q, 0);
							} else { //No singles loaded
								// Load just the left triple
								mktimestr(timestr);
								printf("\nOperator: No single lines are loaded.");
								for (i=0; i<3; i++) {
									pid = removeq(q[1]);
									printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
								}

								for (i=0; i<3; i++) V(semid_lines, 1);

								loadinglt = false;
								V(mutex_q, 0);
							}
						}
					}
				} else { //No triples loaded
					skiersloaded=0;
					mktimestr(timestr);

					if ((loadinglt) && (ltcount > 0)) {
						for (i=0; i<ltcount; i++) {
							pid = removeq(q[1]);
							printf("\nOperator: Loading skier %d from left triple line, PID=%d at %s", (i+1), pid, timestr);
							V(semid_lines, 1);
							skiersloaded++;
						}
						loadinglt = false;
					} else if (rtcount > 0) {
						for (i=0; i<rtcount; i++) {
							pid = removeq(q[2]);
							printf("\nOperator: Loading skier %d from right triple line, PID=%d at %s", (i+1), pid, timestr);
							V(semid_lines, 2);
							skiersloaded++;
						}
						loadinglt = true;
					}
					
					if (skiersloaded<4) {
						if (lsloaded) {
							pid = removeq(q[0]);
							printf("\nOperator: Loading skier from left single line, PID=%d at %s", pid, timestr);
							V(semid_lines, 0);
							skiersloaded++;
						}
					}

					if (skiersloaded<4) {
						if (rsloaded) {
							pid = removeq(q[3]);
							printf("\nOperator: Loading skier from right single line, PID=%d at %s", pid, timestr);
							V(semid_lines, 3);
							skiersloaded++;
						}
					}

					if (skiersloaded<4) {
						printf("\nOperator: Unable to fully load lift, due to unfilled lines.");
					}

					V(mutex_q, 0);
				}
			}
		}

		/* Wait one second for another chair */
		sleep(1);
		P(mutex_count, 0);
	}
	V(mutex_count, 0);

	printf("\n\nAll skiers have left - closing the lift.\n");

	pid = (int)getpid();

	/* Remove the shared memory for the queues */
	for (i=0; i<4; i++) {
		if ((shmid=shmget(pid + i, 0, 0)) == -1) {
			perror("Skier: op shmget of queues failed on cleanup");
			exit(12);
		}
		shmctl(shmid, IPC_RMID, (struct shmid_ds *)0);
	}

	/* Remove the shared memory for the skier count */
	if ((shmid=shmget(pid + 4, 0, 0)) == -1) {
		perror("Skier: op shmget of skier count failed on cleanup");
		exit(13);
	}
	shmctl(shmid, IPC_RMID, (struct shmid_ds *)0);

	/* Remove the four line semaphores */
	semctl(semid_lines, 0, IPC_RMID, 0);

	/* Remove the queue mutex semaphore */
	semctl(mutex_q, 0, IPC_RMID, 0);

	/* Remove the skier count mutex semaphore */
	semctl(mutex_count, 0, IPC_RMID, 0);

	return (0);
}