/*
 * 4220FinalClient.c
 *
 *  Created on: Apr 18, 2015
 *      Author: jch5x7
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#define MSG_SIZE 40			// message size

typedef struct	//all relevant data for a user
{
	char* name;
	char* password;
	char* totalvalue;
}account;

void error(const char *msg)
{
	perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
	int sock, n;
	unsigned int length;
	struct sockaddr_in anybody, from;
	char buffer[MSG_SIZE];	// to store received messages or messages to be sent.
	int boolval = 1;			// for a socket option

	FILE* in;
	char* accountbuf = malloc(sizeof(char)*40);
	int numofacc, count;
	int founduser = 0;

	//Ensure program was initialized with right number of arguments
	if (argc != 3)
	{
		printf("usage: %s port data_file\n", argv[0]);
		exit(1);
	}


	//Open input file to gather data
	in = fopen("accounts.txt" , "r");
	if(in == NULL)
	{
		printf("Input file could not be read\n");
	}

	fgets(accountbuf , 40 , in);
	numofacc = accountbuf[0] - '0';

	account acclist[numofacc];	//list of all known users and their data

	//Read in accounts and there values to be checked against input
	for(count = 0 ; count < numofacc ; count++)
	{
		fgets(accountbuf , 40 , in);
		acclist[count].name = strdup(accountbuf);
		fgets(accountbuf , 40 , in);
		acclist[count].password = strdup(accountbuf);
		fgets(accountbuf , 40 , in);
		acclist[count].totalvalue = strdup(accountbuf);
	}


	sock = socket(AF_INET, SOCK_DGRAM, 0); // Creates socket. Connectionless.
	if (sock < 0)
	{
		error("socket");
	}

	//Change socket permissions to allow broadcast
	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &boolval, sizeof(boolval)) < 0)
	{
		printf("error setting socket options\n");
		exit(-1);
	}

	anybody.sin_family = AF_INET;		// symbol constant for Internet domain
	anybody.sin_port = htons(atoi(argv[1]));				// port field
	anybody.sin_addr.s_addr = inet_addr("10.3.52.255");	// broadcast address

	length = sizeof(struct sockaddr_in);		// size of structure


	//Client loops until administrator either chooses to exit or go to MainMenu
	while(1)
	{
		bzero(buffer, MSG_SIZE);		// clear buffer
		printf("Please enter \'MainMenu\' to start up server programs or ! to exit: ");
		fgets(buffer , sizeof(buffer) , stdin);

		if((strncmp(buffer , "MainMenu" , 8) == 0) || (buffer[0] == '!'))
		{
			break;
		}
	}


	//Triggers when MainMenu was input, alerts user that client is entering an automated response system
	if (buffer[0] != '!')
	{
		n = sendto(sock, buffer, strlen(buffer), 0, (const struct sockaddr *)&anybody,length);
		if (n < 0)
		{
			error("Sendto");
		}
		printf("\n\nThank you for choosing to use our program!\nThe client will not enter an automated mode"
				" to manage the connected servers.\nIf you wish to close the client,\nplease press the ctrl"
				" button and the letter c from your terminal.\n\n");
	}


	while(1)
	{
		//Immediately break loop if client was told to quit
		if (buffer[0] == '!')
		{
			printf("Exiting client system\n");
			break;
		}

		//Receive message
		bzero(buffer,MSG_SIZE);
		n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&from, &length);
		if (n < 0)
		{
			error("recvfrom");
		}

		//When a server closes update the local data file
		if(strcmp(buffer , "@close\0") == 0)
		{
			printf("\nINTERNAL: a server exited, updating data file\n");

			in = fopen("accounts.txt" , "w");
			fprintf(in , "%d\n" , numofacc);
			for(count = 0 ; count < numofacc ; count++)
			{
				fprintf(in , "%s" , acclist[count].name);
				fprintf(in , "%s" , acclist[count].password);
				fprintf(in , "%s" , acclist[count].totalvalue);
			}
			fclose(in);
		}


		//Username was input, search for it and send feedback to server
		else if(strcmp(buffer , "@user\0") == 0)
		{
			printf("\nINTERNAL: checking if user is recognized\n");

			bzero(buffer,MSG_SIZE);
			n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&from, &length);
			if (n < 0)
			{
				error("recvfrom");
			}

			printf("Received something: %s\n", buffer);
			founduser = 0;	//flag determining if user is found is reset before each search

			//Attempt to locate user in array
			for(count = 0 ; count < numofacc ; count++)
			{
				if(strcmp(acclist[count].name , buffer) == 0)
				{
					printf("\nINTERNAL: user found, sending password back\n");
					founduser = 1;

					//The following 5 lines ensure that only the server who sent the message receives the responses, occurs any time client sends sensitive data
					bzero(buffer, MSG_SIZE);
					n = recvfrom(sock, buffer, 10, 0, (struct sockaddr *)&from, &length);
					if (n < 0)
					{
						error("recvfrom");
					}
					anybody.sin_addr.s_addr = inet_addr(buffer);

					bzero(buffer , MSG_SIZE);
					strcpy(buffer , acclist[count].password);
					n = sendto(sock, "found\n", 6 , 0, (const struct sockaddr *)&anybody,length);
					if (n < 0)
					{
						error("Sendto");
					}

					//Sends the server system the password linked to this user
					n = sendto(sock, buffer, strlen(buffer), 0, (const struct sockaddr *)&anybody,length);
					if (n < 0)
					{
						error("Sendto");
					}
					break;
				}
			}

			//If user not found, send main menu
			if(founduser == 0)
			{
				printf("\nINTERNAL: user not found\n");

				//Updating who recipient is
				bzero(buffer, MSG_SIZE);
				n = recvfrom(sock, buffer, 10, 0, (struct sockaddr *)&from, &length);
				if (n < 0)
				{
					error("recvfrom");
				}
				anybody.sin_addr.s_addr = inet_addr(buffer);

				n = sendto(sock, "notfound\n", 9, 0, (const struct sockaddr *)&anybody,length);
				if (n < 0)
				{
					error("Sendto");
				}

				n = sendto(sock, "MainMenu\n", 9, 0, (const struct sockaddr *)&anybody,length);
				if (n < 0)
				{
					error("Sendto");
				}
			}
		}


		//Password was correct, send user there funds so that they can alter them and give them a new menu
		else if(strcmp(buffer , "@rightpass\0") == 0)
		{
			printf("\nINTERNAL: User's password was correct, proceeding to fund access.\n");

			//Updating who recipient is
			bzero(buffer, MSG_SIZE);
			n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&from, &length);
			if (n < 0)
			{
				error("recvfrom");
			}
			anybody.sin_addr.s_addr = inet_addr(buffer);

			//Send server system this user's total funds
			bzero(buffer,MSG_SIZE);
			strcpy(buffer , acclist[count].totalvalue);
			n = sendto(sock, buffer, MSG_SIZE, 0, (const struct sockaddr *)&anybody,length);
			if (n < 0)
			{
				error("Sendto");
			}

			bzero(buffer,MSG_SIZE);
			strcpy(buffer , "MoneyMenu\n");
			n = sendto(sock, buffer, MSG_SIZE, 0, (const struct sockaddr *)&anybody,length);
			if (n < 0)
			{
				error("Sendto");
			}
		}

		//Password was not found, return user to main menu to try again if they wish
		else if(strcmp(buffer , "@wrongpass\0") == 0)
		{
			printf("\nINTERNAL: User's password was incorrect, closing login attempt.\n");

			//Updating who recipient is
			bzero(buffer, MSG_SIZE);
			n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&from, &length);
			if (n < 0)
			{
				error("recvfrom");
			}
			anybody.sin_addr.s_addr = inet_addr(buffer);

			n = sendto(sock, "MainMenu\n", 9, 0, (const struct sockaddr *)&anybody,length);
			if (n < 0)
			{
				error("Sendto");
			}
		}

		//User logs out and new funds is received, return to MainMenu
		else if(strcmp(buffer , "@logout\0") == 0)
		{
			printf("\nINTERNAL: User logged out, taking in there updated funds to save to records.\n");

			//Receive the altered funds and store them back into the array
			bzero(buffer , MSG_SIZE);
			n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&from, &length);
			if (n < 0)
			{
				error("recvfrom");
			}

			strcpy(acclist[count].totalvalue , buffer);

			//File is updated when a user logs out, so if user logs in somewhere else, their updated funds will be available
			in = fopen("accounts.txt" , "w");
			fprintf(in , "%d\n" , numofacc);
			for(count = 0 ; count < numofacc ; count++)
			{
				fprintf(in , "%s" , acclist[count].name);
				fprintf(in , "%s" , acclist[count].password);
				fprintf(in , "%s" , acclist[count].totalvalue);
			}
			fclose(in);

			//Updating who recipient is
			bzero(buffer, MSG_SIZE);
			n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&from, &length);
			if (n < 0)
			{
				error("recvfrom");
			}
			anybody.sin_addr.s_addr = inet_addr(buffer);

			n = sendto(sock, "MainMenu\n", 9, 0, (const struct sockaddr *)&anybody,length);
			if (n < 0)
			{
				error("Sendto");
			}
		}
	}

	//Write list back to file with updated values to be used upon next startup if client is closed
	in = fopen("accounts.txt" , "w");
	fprintf(in , "%d\n" , numofacc);
	for(count = 0 ; count < numofacc ; count++)
	{
		fprintf(in , "%s" , acclist[count].name);
		fprintf(in , "%s" , acclist[count].password);
		fprintf(in , "%s" , acclist[count].totalvalue);
	}
	fclose(in);


	close(sock);// close socket.
	return 0;
}
