#include "comn.h"

void UserConn();
void UserDisConn();
void UserChat();
void UserOut();

void setSignal();

int main(int argc, char* argv[]) {

	printf("\n[ client process ]\n");
	setSignal();
	shmid = shmget((key_t)0301, sizeof(shData), IPC_EXCL);
	if (shmid == -1) {
		printf("### no server process ###\n");
		exit(1);
	}

	if (shmctl(shmid, IPC_STAT, &info) == -1) {
		printf("### shmctl fail ###");
		exit(1);
	}

	if (argc < 2) {
		printf("### write user name ###\n");
		exit(1);
	}


	printf("user count : %d\n", info.shm_nattch);

	if (info.shm_nattch > 3) {
		printf("### max user in. sorry ###\n");
		exit(1);
	}

	shmaddr = shmat(shmid, (void*)0, 0);
	if (shmaddr == (void*)-1) {
		printf("### shmat fail ###\n");
		exit(1);
	}

	shData = (struct SharedData*)shmaddr;

	if (strlen(argv[1]) > MAX_USER_NAME) {
		printf("### user name max size 10 ###\n");
		exit(1);
	}

	int i = 0;
	for (i = 0; i < MAX_USER_CNT; i++) {
		if (shData->client[i].pid != 0) {
			if (strcmp(shData->client[i].name, argv[1]) == 0) {
				printf("### [ %s ] is duplicate user name ###\n", argv[1]);
				exit(1);
			}
		}
	}

	if (shData->client[0].pid == 0) {
		shData->client[0].pid = getpid();
		sprintf(shData->client[0].name, "%s", argv[1]);
	} else {
		shData->client[1].pid = getpid();
		sprintf(shData->client[1].name, "%s", argv[1]);
	}

	pid_t server_pid = shData->pid;

	kill(server_pid, SIGUSR1);

	// message queue create
	if ((msqid = msgget((key_t)4225, IPC_CREAT | 0666)) == -1) {
		printf("msgget fail\n");
		exit(1);
	}

	char msg_buf[MAX_CHAT_LENGTH];

	printf("=====================\n");
	printf(" user enter chatting\n");
	printf("=====================\n");

	while (1) {
		if ((shData->client[0].pid != 0) && (shData->client[1].pid != 0)) {
			memset(chat.message, 0, sizeof(chat.message));
			memset(msg_buf, 0, sizeof(msg_buf));

			printf("> ");

			fgets(msg_buf, sizeof(msg_buf), stdin);	// input buf

			// send user info
			chat.send_pid = getpid();
			strcpy(chat.message, msg_buf);

			if (strlen(chat.message) != 0) {
				// chat -> message queue
				if ((msgsnd(msqid, &chat, sizeof(chat), 0)) == -1) {
					printf("### msgsnd fail ###\n");
					exit(1);
				}

				// show chat to other user
				if (shData->client[0].pid == getpid()) {
//					printf("%s> %s\n", shData->client[0].name, msg_buf);
					kill(shData->client[1].pid, SIGUSR2);
				} else {
//					printf("%s> %s\n", shData->client[1].name, msg_buf);
					kill(shData->client[0].pid, SIGUSR2);
				}
			}
		}
		sleep(1);
	}

	return 0;
}

void UserConn() {
	if (shData->client[1].pid == 0) {
		printf("\n [ %s ] entered.\n", shData->client[0].name);
	} else {
		printf("\n [ %s ] entered.\n", shData->client[1].name);
	}
	printf("-------------------------\n");
}

void UserChat() {
	// message queue -> chat
	if ((msgrcv(msqid, &chat, sizeof(chat), 0, 0)) == -1) {
		printf("### msgrcv fail ###\n");
		exit(1);
	}
	
	shData = (struct SharedData*)shmaddr;

	if (chat.send_pid == shData->client[0].pid) {
		printf("\n%s >> %s\n", shData->client[0].name, chat.message);
	} else {
		printf("\n%s >> %s\n", shData->client[1].name, chat.message);
	}
}

void UserDisConn() {
	pid_t server_pid = shData->pid;

	if (shmctl(shmid, IPC_STAT, &info) == -1) {
		printf("### shmctl fail ###");
		exit(1);
	}

	if (info.shm_nattch < 3) {
		// message queue delete
		if (msgctl(msqid, IPC_RMID, 0) == -1) {
			printf("### msgctl fail ###\n");
			exit(1);
		}
	}

	if (shData->client[0].pid == getpid()) {
		shData->client[0].pid = -1;
		kill(server_pid, SIGALRM);
		kill(shData->client[1].pid, SIGALRM);
		printf("\n ### user [ %s ] out ###\n", shData->client[0].name);
		exit(1);
	} else {
		shData->client[1].pid = -1;
		kill(server_pid, SIGALRM);
		kill(shData->client[0].pid, SIGALRM);
		printf("\n### user [ %s ] out ###\n", shData->client[1].name);
		exit(1);
	}
}

void UserOut() {
	int i = 0;
	for (i = 0; i < MAX_USER_CNT; i++) {
		if (shData->client[i].pid != getpid()) {
			printf("\n ### user [ %s ] out ###\n", shData->client[i].name);
		}
	}
}

void setSignal() {

	struct sigaction chat_signal;
	struct sigaction conn_sign;
	struct sigaction disconn_sign;
	struct sigaction leave_signal;

	chat_signal.sa_handler = UserChat;
	sigemptyset(&chat_signal.sa_mask);
	chat_signal.sa_flags = 0;
	sigaction(SIGUSR2, &chat_signal, NULL);

	conn_sign.sa_handler = UserConn;
	sigemptyset(&conn_sign.sa_mask);
	conn_sign.sa_flags = 0;
	sigaction(SIGUSR1, &conn_sign, NULL);

	leave_signal.sa_handler = UserOut;
	sigemptyset(&leave_signal.sa_mask);
	leave_signal.sa_flags = 0;
	sigaction(SIGALRM, &leave_signal, NULL);

	disconn_sign.sa_handler = UserDisConn;
	sigemptyset(&disconn_sign.sa_mask);
	disconn_sign.sa_flags = 0;
	sigaction(SIGINT, &disconn_sign, NULL);
}
