/*
 * 4220FinalServer.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 <sys/mman.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>

#define MSG_SIZE 40			// message size

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

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

	struct hostent* thishost;	//variables needed for storing this server's ip
	char hostptr[64];
	size_t hostlen = 64;
	char* myip;

	char mainmenu[20];		//various variable and flags used in handling user input
	char moneymenu[20];
	char thisuser[20];
	char chkpass [5] = "0000";
	int passcount = 0;
	unsigned long password = 0;
	long currmoney = 0;
	char chkmoney[20];
	char* ismoneyammount;
	long adjmoney;

	//Ensures right number of arguments were received
	if (argc < 2)
	{
		printf("usage: %s port\n", argv[0]);
		exit(0);
	}

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


	//Setup for button input later
	int fd = open("/dev/mem", O_RDWR);

	if (fd < 0)
	{
		printf("Unable to open the file.\n");
		exit(1);
	}
	unsigned long* ptr = (unsigned long*)mmap(NULL , getpagesize(),PROT_READ|PROT_WRITE , MAP_SHARED , fd , 0x80840000);

	unsigned long* BDR = ptr + 1;
	unsigned long* BDDR = ptr + 5;
	unsigned long* BDBounce = ptr + 49;

	*BDDR = *BDDR & 0xffffffe0;		//set buttons to inputs and activate DBounce for them
	*BDBounce = *BDBounce & 0x0000001f;


	length = sizeof(server);			// length of structure
	bzero(&server,length);			// clear structure
	server.sin_family = AF_INET;		// symbol constant for Internet domain
	server.sin_addr.s_addr = INADDR_ANY;		// IP address of this machine
	server.sin_port = htons(atoi(argv[1]));	// port number

	//Binds the socket to the address of the host and the port number
	if (bind(sock, (struct sockaddr *)&server, length) < 0)
	{
		error("binding");
	}

	//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);
	}

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

	//Get this IP address of this server to be sent to the client
	gethostname(hostptr , hostlen);	//fill the hostent struct
	thishost = gethostbyname(hostptr);

	myip = strdup(inet_ntoa(*((struct in_addr*)thishost->h_addr_list[0])));	//this is used as the return address the client is told to send responses to


	while (1)
	{
		bzero(buffer,MSG_SIZE);	// empty buffer

		//Receive messages from client program
		n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&addr, &fromlen);
		if (n < 0)
		{
			error("recvfrom");
		}

		//Run the main menu system
		if(strcmp(buffer , "MainMenu\n") == 0)
		{
			while(1)
			{
				bzero(mainmenu , 20);
				printf("\n===Welcome to The Online Bank!===\n Please select an option\n  1)Log in\n  2)Exit\nYour choice: ");
				fgets(mainmenu , sizeof(mainmenu) , stdin);

				//User inputs their username, client is alerted that a username is coming along with where the response should be sent back to
				if(strcmp(mainmenu , "1\n") == 0)
				{
					bzero(thisuser , 20);
					printf("Please enter your username: ");
					fgets(thisuser , sizeof(thisuser) , stdin);

					n = sendto(sock , "@user" , MSG_SIZE , 0 , (struct sockaddr*)&addr , fromlen);
					if(n < 0)
					{
						error("sendto");
					}

					n = sendto(sock , thisuser , MSG_SIZE , 0 , (struct sockaddr*)&addr , fromlen);
					if(n < 0)
					{
						error("sendto");
					}

					n = sendto(sock , myip , 10 , 0 , (struct sockaddr*)&addr , fromlen);
					if(n < 0)
					{
						error("sendto");
					}
					break;
				}

				//Program is closed, notify client that server is shutting down
				else if(strcmp(mainmenu , "2\n") == 0)
				{
					printf("Thank you for using The Online Bank!\nInitiating shutdown.\n");
					n = sendto(sock , "@close\0" , 7 , 0 , (struct sockaddr *)&addr , fromlen);
					if(n < 0)
					{
						error("sendto");
					}
					return 0;
				}
			}
		}


		//Username was found, program now prompts the user to input their password
		else if(strcmp(buffer , "found\n") == 0)
		{
			bzero(buffer , MSG_SIZE);
			n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&addr, &fromlen);
			if (n < 0)
			{
				error("recvfrom");
			}

			printf("\nWelcome back! Please enter your password: ");
			fflush(stdout);

			//Getting password from button presses, also shows numerically what users inputs on screen
			while(passcount < 4)
			{
				msync(ptr , getpagesize() , MS_INVALIDATE);
				if((*BDR | 0xfffffffe) == 0xfffffffe)
				{
					printf("1");
					fflush(stdout);
					usleep(1000000);
					password = password * 10 + 1;
					passcount++;
				}
				msync(ptr , getpagesize() , MS_INVALIDATE);
				if((*BDR | 0xfffffffd) == 0xfffffffd)
				{
					printf("2");
					fflush(stdout);
					usleep(1000000);
					password = password * 10 + 2;
					passcount++;
				}
				msync(ptr , getpagesize() , MS_INVALIDATE);
				if((*BDR | 0xfffffffb) == 0xfffffffb)
				{
					printf("3");
					fflush(stdout);
					usleep(1000000);
					password = password * 10 + 3;
					passcount++;
				}
				msync(ptr , getpagesize() , MS_INVALIDATE);
				if((*BDR | 0xfffffff7) == 0xfffffff7)
				{
					printf("4");
					fflush(stdout);
					usleep(1000000);
					password = password * 10 + 4;
					passcount++;
				}
				msync(ptr , getpagesize() , MS_INVALIDATE);
				if((*BDR | 0xffffffef) == 0xffffffef)
				{
					printf("5");
					fflush(stdout);
					usleep(1000000);
					password = password * 10 + 5;
					passcount++;
				}
			}

			//Convert input password to string and determine next step in program
			bzero(chkpass, 5);
			snprintf(chkpass , sizeof(chkpass)+1, "%lu", password);
			passcount = 0;
			password = 0;

			if(strncmp(chkpass , buffer , 4) == 0)
			{
				printf("\nPASSWORD ACCEPTED\n");

				n = sendto(sock , "@rightpass" , MSG_SIZE , 0 , (struct sockaddr*)&addr , fromlen);
				if(n < 0)
				{
					error("sendto");
				}

				n = sendto(sock , myip , 10 , 0 , (struct sockaddr*)&addr , fromlen);
				if(n < 0)
				{
					error("sendto");
				}

				//Receive the account funds, next menu prompt is handled by main recvfrom
				bzero(buffer , MSG_SIZE);
				n = recvfrom(sock, buffer, MSG_SIZE, 0, (struct sockaddr *)&addr, &fromlen);
				if (n < 0)
				{
					error("recvfrom");
				}

				//This will turn money to a long and notify user how much money they have access to
				currmoney = strtol(buffer , (char**)NULL , 10);
				printf("Your current balance is: %lu\n" , currmoney);
			}

			else
			{
				printf("\n\nI'm sorry, this password is not correct. Returning to main menu.\n\n");

				n = sendto(sock , "@wrongpass" , MSG_SIZE , 0 , (struct sockaddr*)&addr , fromlen);
				if(n < 0)
				{
					error("sendto");
				}

				n = sendto(sock , myip , 10 , 0 , (struct sockaddr*)&addr , fromlen);
				if(n < 0)
				{
					error("sendto");
				}
			}
		}

		//Username was not connected to a known user, return to MainMenu is handled by main recvfrom
		else if(strcmp(buffer , "notfound\n") == 0)
		{
			printf("\nSorry, we could not find you in our database.\n\n");

			n = sendto(sock , myip , 10 , 0 , (struct sockaddr*)&addr , fromlen);
			if(n < 0)
			{
				error("sendto");
			}
		}

       //Once user is successfully logged in, allow them to alter funds
       else if(strcmp(buffer , "MoneyMenu\n") == 0)
       {
    	   while(1)
    	   {
    		   bzero(moneymenu , 20);
    		   printf("\n===Welcome back user!===\n Please select an option\n  1)Add Funds\n  2)Withdraw Funds\n  3)Logout\nYour choice: ");
    		   fgets(moneymenu , sizeof(moneymenu) , stdin);

    		   //Deposit funds
    		   if(strcmp(moneymenu , "1\n") == 0)
    		   {

    			   printf("Please enter amount you are depositing: ");
        		   fgets(chkmoney , sizeof(chkmoney) , stdin);
        		   adjmoney = strtol(chkmoney , &ismoneyammount , 10);

        		   if (chkmoney[0] != '\n' && (*ismoneyammount == '\n' || *ismoneyammount == '\0'))
        		   {
        			   currmoney = currmoney + adjmoney;
        		   }
        		   else
        		   {
        			   printf("Sorry, you did not enter a number. Please try again.\n");
        		   }
    		   }

    		   //Withdrawing funds
    		   else if(strcmp(moneymenu , "2\n") == 0)
    		   {
    			   printf("Please enter the amount you wish to withdraw: ");
        		   fgets(chkmoney , sizeof(chkmoney) , stdin);
        		   adjmoney = strtol(chkmoney , &ismoneyammount , 10);

        		   if (chkmoney[0] != '\n' && (*ismoneyammount == '\n' || *ismoneyammount == '\0'))
        		   {
        			   if((currmoney - adjmoney) > 0)
        			   {
        				   currmoney = currmoney - adjmoney;
        			   }
        			   else
        			   {
        				   printf("Sorry, you do not have the required funds for this withdrawal.\n");
        			   }
        		   }
        		   else
        		   {
        			   printf("Sorry, your transaction could not be completed as entered. Please try again.\n");
        		   }
    		   }

    		   //Send the client this user's funds and get back to main menu
    		   else if(strcmp(moneymenu , "3\n") == 0)
    		   {
    		   	   printf("Thank you for using The Online Bank!\nReturning to mainmenu...\n\n");

            	   n = sendto(sock , "@logout\0" , 8 , 0 , (struct sockaddr*)&addr , fromlen);
    		   	   if(n < 0)
    		   	   {
    			   	   error("sendto");
    		   	   }

				   //Turn the funds back into a string and send them to client
    		   	   bzero(buffer , MSG_SIZE);
    		   	   snprintf(buffer , MSG_SIZE, "%lu", currmoney);
    		   	   strcat(buffer , "\n");
            	   n = sendto(sock , buffer , MSG_SIZE , 0 , (struct sockaddr*)&addr , fromlen);
    		   	   if(n < 0)
    		   	   {
    			   	   error("sendto");
    		   	   }
            	   n = sendto(sock , myip , 10 , 0 , (struct sockaddr*)&addr , fromlen);
            	   if(n < 0)
            	   {
            		   error("sendto");
            	   }
    		   	   break;
    		   }
    	   }
       }
   }

   return 0;
 }
