/* tm.c : Turing Machine Simulator */
/* (c)2002 J.D. White, written in MMU Melaka by*/
/* Soh Sun Liang, Chin Lu Chee, Tang Yee Rou, */
/* Lydia Lim Tze Yee, Katherine Chuo Wee Jinq */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#define MSTATE 32 /*Maximum length of the name of a state */
#define MFILE 256 /*Maximum length of the name of a file*/
#define MLINE 256 /*Maximum length of a line in the control file */

struct Rules {  /* TM Controller Rules */
	char currState[MSTATE];
	char input; 
	char output;
	char direction;
	char newState[MSTATE];
	struct Rules *next;
};

struct Tape {  /* Tape Node */
	char mark;
	struct Tape *previous;
	struct Tape *next;
};

struct Rules *initRules(char *currState, char input, 
	char output, char direction, char *newState);
struct Rules *extractRules(FILE *fCtrl); /*extract rules from control file*/
struct Tape *initTape(char mark);
struct Tape *extractTape(FILE *fTape);
void printTape( struct Tape *, struct Tape * );

int main(int argc,char *argv[]){
	FILE *fCtrl; /* turing machine specification file */ 
	FILE *fTape; /* tape input to turing machine*/ 
	FILE *fOut; /*tape after running turing machine */
	int errCode=0;
	int cntr=1,flg_success=1;
	char currState[MSTATE];
	struct Rules *head, *curr;
	struct Tape *currPosition, *startPosition;
	char tapeFileName[MFILE],ctrlFileName[MFILE],longFileName[MFILE], cdir='b';
	
	/* 1. read and validate input information */
	fprintf(stdout,"tm.c : Turing Machine Simulator\n"
		"(c)2002 J.D. White, written in MMU Melaka by\n"
		"Soh Sun Liang, Chin Lu Chee, Tang Yee Rou,\n"
		"Lydia Lim Tze Yee, Katherine Chuo Wee Jinq\n\n");

	if(argc!=3){ 
		fprintf(stderr,"Usage: turing <turing machine rules file> <tape file> \n"
			"\tSample files: 'turing add1.tm add1.tap'\n"
			"\t              'turing duplicat.tm duplicat.tap'\n"
			"\t              'turing inc.tm inc.tap'\n" 
			"\n");
		fprintf(stdout,"Input file name for Turing machine controller: ");
		fgets(ctrlFileName,MFILE -1,stdin);
		ctrlFileName[strlen(ctrlFileName)-1]='\0';
		fprintf(stdout,"Input file name for Input Tape: ");
		fgets(tapeFileName,MFILE-1,stdin);
		tapeFileName[strlen(tapeFileName)-1]='\0';		
	}else{
		strcpy(ctrlFileName,argv[1]);
		strcpy(tapeFileName,argv[2]);
	}

	fprintf(stderr,"1. Loading controller from file '%s'...\n",ctrlFileName);
	sprintf(longFileName,"%s",ctrlFileName);
	while((fCtrl=fopen(longFileName,"r"))==NULL && cdir < 'j'){
		cdir++;
		sprintf(longFileName,"%c:\\code\\c12\\%s",cdir,ctrlFileName);
	}
	if(fCtrl==NULL){errCode=1;goto error;}  
	head = extractRules(fCtrl);
	fclose(fCtrl);
	if(head->next==NULL){errCode=2;goto error;}
	fputs("\nOK, Controller loaded.\n",stderr);
	
	
	fprintf(stderr,"2. Reading tape file '%s'...",tapeFileName);
	cdir='b';
	sprintf(longFileName,"%s",tapeFileName);
	while((fTape=fopen(longFileName,"r"))==NULL && cdir < 'j'){
		cdir++;
		sprintf(longFileName,"%c:\\code\\c12\\%s",cdir,tapeFileName);
	}
	if(fTape==NULL){errCode=3; goto error;}  
	startPosition=extractTape(fTape);
	fclose(fTape);
	fputs("OK.\n",stderr);
	
	fputs("3. Running Turing Machine (press Enter to continue to next line)...\n" 
		"Current position of head denoted by [ ]\n\n",stderr);
	strcpy(currState,head->next->currState);
	printf("Step %d\tState: %s\tTape: ",0,currState);
	printTape(startPosition,currPosition);
	currPosition=startPosition;
	while(flg_success){
		curr=head->next;
		flg_success=0;
		while(curr!=NULL){
			if(currPosition->mark==curr->input
					&& strcmp(currState,curr->currState)==0){
				printf("Step %d\tState: %s\tTape: ",cntr,currState);
				printTape(startPosition,currPosition);
				currPosition->mark=curr->output;
				if(curr->direction=='<'){
					if(currPosition->previous==NULL){
						currPosition->previous=initTape(' ');
						currPosition->previous->next=currPosition;
						startPosition=currPosition->previous;
					}
					currPosition=currPosition->previous;
					if(currPosition->next->mark==' '
							&& currPosition->next->next==NULL){
						free(currPosition->next);
						currPosition->next=NULL;
					}
				}
				else if(curr->direction=='>'){
					if(currPosition->next==NULL){
						currPosition->next=initTape(' ');
						currPosition->next->previous=currPosition;
					}
					currPosition=currPosition->next;
					if(currPosition->previous->mark==' '
							&& currPosition->previous->previous==NULL){
						currPosition->previous=NULL;
					}
				}
				strcpy(currState,curr->newState);
				flg_success=1;
				cntr++;
				break;
			}
			curr=curr->next;
		}
	}
	printf("Step %d\tState: %s\tTape: ",cntr,currState);
	printTape(startPosition,currPosition);
	printf("\n");
	printf("HALT\n");

	fputs("4. Writing new tape to file 'new.tap'...",stderr);
	currPosition=startPosition;
	fOut=fopen("new.tap","w");
	if(fOut!=NULL){
		while(currPosition->mark==' ')
			currPosition=currPosition->next;
		while(currPosition!=NULL){
			fputc(currPosition->mark,fOut);
			currPosition=currPosition->next;
		}	
		fclose(fOut);
		fputs("OK\n",stderr);
	}else{errCode=4; goto error;}
	fgetc(stdin);
	return(errCode);
error:
	switch (errCode){
		case 1:
			fprintf(stderr,"ERROR Cannot locate file '%s'.  Does it exist? \nIf running Windows Explorer, try to Open command window and run",ctrlFileName);
			break;
		case 2:	
			fprintf(stderr,"ERROR file '%s' has an improper format\n",ctrlFileName);
			break;
		case 3:
			fprintf(stderr,"ERROR Cannot locate file '%s'.  Does it exist? \nIf running Windows Explorer, try to Open command window and run",tapeFileName);
			break;
		case 4: 
			fprintf(stderr,"ERROR Unable to open 'new.tap' for output\nIs disk write protected" 
				"or file open in another software?/n");
			break;
		default:
			fprintf(stderr,"Other Error.");
	}
	fgetc(stdin);
	return(errCode);
}

/* Function initRules: Adds a new rule */
struct Rules *initRules(char *currState,char input,
		char output,char direction,char *newState){
	struct Rules *temp;
	temp=(struct Rules *)malloc(sizeof(struct Rules));
	strcpy(temp->currState,currState);
	temp->input=input;
	temp->output=output;
	temp->direction=direction;
	strcpy(temp->newState,newState);
	temp->next=NULL;
	return(temp);
}

/* Function extractRules: Extract rules data from string */
struct Rules *extractRules(FILE *fCtrl){
	struct Rules *head, *curr;
	char buff;
	char line[MLINE];
	int counter;
	char currState[MSTATE],input,output,direction,newState[MSTATE];
	int comment=0; /* FLAG. mark comment starting */

	head=initRules("",'\0','\0','\0',"");
	curr=head;

	while((buff=fgetc(fCtrl))!=EOF){
		if(buff==';')comment=1;
		if(buff=='\n')comment=0;
		if(buff=='(' && comment==0){
/*get currState*/
			counter=0;
			while((buff=fgetc(fCtrl))==' ');
			currState[counter]=buff;
			buff=fgetc(fCtrl);
			while(buff!=' ' && buff!=','){
				counter++;
				currState[counter]=buff;
				buff=fgetc(fCtrl);
			}
			counter++;
			currState[counter]='\0';
			if(buff==' ')
				while((buff=fgetc(fCtrl))!=',');
/*get input, output and direction*/
			input=' ';
			while((buff=fgetc(fCtrl))!=',')
				if(!isspace(buff))input=buff;
			output=' ';
			while((buff=fgetc(fCtrl))!=',')
				if(!isspace(buff))output=buff;
			direction=' ';
			while((buff=fgetc(fCtrl))!=',')
				if(!isspace(buff))direction=buff;
/*get newState*/
			counter=0;
			while((buff=fgetc(fCtrl))==' ');
			newState[counter]=buff;
			buff=fgetc(fCtrl);
			while(buff!=' ' && buff!=')'){
				counter++;
				newState[counter]=buff;
				buff=fgetc(fCtrl);
			}
			counter++;
			newState[counter]='\0';	
/*create a node and append it to the end of the linked list */
			curr->next=initRules(currState,input,output,direction,newState);
			curr=curr->next;
		}
	}
	rewind(fCtrl);
	while(fgets(line,MLINE-1,fCtrl)!=NULL)
		fprintf(stderr,"     %s",line);	
	return(head);
}

/*Function initTape: Adds a new tape node */
struct Tape *initTape(char mark){
	struct Tape *temp=(struct Tape * )calloc(1,sizeof(struct Tape));
	temp->mark=mark;
	return(temp);
}

struct Tape *extractTape(FILE *fTape) { /* Extract tape data from string*/
	struct Tape *head, *curr;
	char buff;
	buff=fgetc(fTape);
	while(isspace(buff))buff=fgetc(fTape);
	head=initTape(buff);
	curr=head;
	buff=fgetc(fTape);
	while(!isspace(buff) && buff!=EOF){
		curr->next=initTape(buff);
		curr->next->previous=curr;
		curr=curr->next;
		buff=fgetc(fTape);
	}
	return(head);
}

/*function printTape: Print the tape out*/
void printTape(struct Tape *head,struct Tape *curr){
	struct Tape *temp=head;
	do{
		if(temp==curr)printf("[%c]",temp->mark);
		else printf(" %c ",temp->mark);
		temp=temp->next;
	}while(temp!=NULL);
	fgetc(stdin); /* throw away character*/
	return;
}
