// Messenger.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>


#define MAX_TEXT_SIZE 1024
#define MAX_NAME_SIZE 32

#define true 1
#define false 0

#define USE_ECHO 0  /*Must be set to 0 for proper display, which actually messes up the display afterward!*/ 

char myName[MAX_NAME_SIZE]="";  /*User name>*/
char myMessage[MAX_TEXT_SIZE];   /**/

char *lockFile="//tmp//default.lock"; /*A shared directory is suitable for real usage. Be careful to always erase this lock when not needed anymore*/
char *messageFile="//tmp//default.message.dat"; /*A shared directory is suitable for real usage*/
char *openMessengersCommandLine="ps -edf|grep messenger| grep -v grep|tr -s ' ' | cut -d \" \" -f 2";

pthread_mutex_t sendMessageMutex; /*May be needed somewhere...*/

int received;

void StartListening(void);  /*Declares the function. Actual function defined later*/

int set_raw_mode(int echo) /*Disable keyboard input buffering.*/ 
{
	int fd = STDIN_FILENO;
	struct termios t;

	if (tcgetattr(fd, &t) < 0)
	{
		perror("tcgetattr");
		return -1;
	}
	if (echo)
		t.c_lflag &= ~(ICANON); //Turn on Echo
	else
		t.c_lflag &= ~(ICANON|ECHO); //Turn off Echo	
	if (tcsetattr(fd, TCSANOW, &t) < 0)
	{
		perror("tcsetattr");
		return -1;
	}
	setbuf(stdin, NULL);
	return 0;
}


int InitUserName(int argc,char *argv[]) /*Check user name*/
{
	if (argc<2) /*Check that a name is passed to the program*/
	{
		printf("Giv'me your name dude!\n");
		return 1; 
	}
	if (strlen(argv[1])+2>MAX_TEXT_SIZE) /*Test that name is not too long*/
	{
		printf("You've got a long name dude!\n");
		return 1; 
	}
	strcpy(myName,argv[1]);
	strcat(myName,">"); /*Add a prompt to the user name*/
	return 0;
}

char GetKey() /*Read a keyboard input*/
{
	char c;
	return((read(0, &c, 1) > 0) ? c: 0);
	//return 0;
}

void GetLock(char *lockName) /*Use a file as a lock*/
{
	int d,cpt=0;
	d=-1;
	while (d==-1) {
		d=open(lockName, O_WRONLY | O_CREAT | O_EXCL);/*O_EXCL: Refuse opening a file if already open*/
		cpt++;
		sleep(0); /*A delay is suitable here, but the sleep function only handles seconds...*/
	}
	close (d);
}

void ReleaseLock(char *lockName) /*Release the lock*/
{
	char command[MAX_TEXT_SIZE];
	sprintf(command,"rm %s>/dev/null",lockName);/*Creates a command name. Output redirected to /dev/null*/
	FILE *p=popen(command,"w");/*Call the command*/
	if (p!=NULL) 
	{
		fflush (p);
		pclose(p);
	}
}

void WriteMessage(char *message, int eraseLine, int goToLine) 
{
	/*lock screen here*/
	int i;
	if (eraseLine)
		for(i=0;i<MAX_TEXT_SIZE/8;i++)
			printf("\b\b\b\b\b\b\b\b");/*Erase last line. Maximum size line supposed to be MAX_TEXT_SIZE*/
	printf("%s",message);
	if (goToLine)
		printf("\n\n");
	fflush(stdout);
	/*unlock screen here*/
}

void SendMessageToPeers(char *message)   /*Function called when enter is pressed*/
{
	FILE *f;
	char s[2048];
	int pid;

	GetLock(lockFile); /*Only a process at a time can send to other processes*/
	f=fopen(messageFile,"w");  /* Start writing pid + message into a temporary file for Inter process communication. Very simple*/
	fprintf(f,"%d %s",getpid(),message);
	fclose(f);  /*pid + message written*/
	f=popen(openMessengersCommandLine,"r");  /*Get the pids of all the programs named Messenger*/
	if (f==NULL)
	{
		ReleaseLock(lockFile);
		perror("error popen\n");
		exit(1);
	}
	while (!feof(f))  /*Loop on all the pids, including itself*/
	{
		pid=fscanf(f,"%s\n",s); /*Read a single pid*/
		if (pid!=-1) 
		{	/* a pid has been read*/
			sscanf(s,"%d",&pid);  /*Converts pid string to integer*/
			received=false;		/*Will be set to 1 when the receiver send a signal to acknowledge reception*/
			int ret_val=kill(pid,SIGUSR1); /*Sends a message to another Messenger process*/
			if (ret_val==0) /*Signal sending is ok*/
				while(!received) 
					sleep(0);  /*A few milliseconds would be best*/
		}
	}
	fclose (f); /*Close the pipe*/
	ReleaseLock(messageFile); /*Actually just for deleting the message. No lock functionality here*/
	ReleaseLock(lockFile);/*Release the lock*/
}

void SendMessageThread()
{
	char message[MAX_TEXT_SIZE];
	strcpy(message,myName);  
	strcat(message,myMessage);	/*Appends message to user name*/
	myMessage[0]=0;				/*Re-initializes inputted text*/
	SendMessageToPeers(message);/*CreateThread() here instead*/
}

void KeyboardInputManager()  /*loop to get inputted characters from the user*/
{
	char key;
	int l;

	while (1) 
	{
		key=GetKey();
		switch (key) {
			case 0:
				break;
			case 10:  /*Send Message to processes*/
				
				SendMessageThread();
				break;
			case 27: /*Escape key*/ 
				raise(SIGINT);
			default:
				l=strlen(myMessage)/*+strlen(myName)*/;
				if (l<MAX_TEXT_SIZE-1)
				{
					myMessage[l]=key;
					myMessage[l+1]=0;
					if (!USE_ECHO)
					{
						printf("%c",key); /*Need to display the inputted character if echo has been disabled*/
						fflush(stdout);
					}
				}
				break;
		}
	}
}

void Listening(int sig)
{
		char message[MAX_TEXT_SIZE];
		int pid;
		int nbRead;
		FILE *f;

		switch (sig) {
			 case SIGUSR1:
				f=fopen(messageFile,"r");
				fscanf(f,"%d ",&pid);
				nbRead=fread(message,1,MAX_TEXT_SIZE,f);
				message[nbRead]=0;
				fclose(f);
				WriteMessage(message,true,true);
				WriteMessage(myMessage,false,false);
				sleep(0);
				kill(pid,SIGUSR2);
				break;
			case SIGUSR2:
				received=true;
				break;
		}
}

void StartListening(void)
{
	signal(SIGUSR1,Listening);
	signal(SIGUSR2,Listening);
}

int main(int argc, char* argv[])
{
	if (InitUserName(argc,argv)) /*Check that argv[1] is valid and copy Name to myName variable*/
		exit(1);
	set_raw_mode(USE_ECHO);
	StartListening();   
	KeyboardInputManager();
	return 0;
}
