#include <stdint.h>
#include <time.h>
#include <stdlib.h>
#include <fstream>
#include <vector>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <math.h> 
#include <stdio.h> 
#include <unistd.h>

using namespace std;

//function prototypes
void getTime(char* timeString);
void getWhitelist(vector <int> &whitelist);
bool testKey(vector <int> &whitelist, int keyTry);	//done
void network(vector <int> &whitelist);
void accLog(bool pass, int keyTry, sockaddr_in &clientIP);
int getKeyID(char* data);	//Used to search in whitelist for ID
void storeEncryption(char* data);	//Used to store info in client data
int hexToDec(string hexa);

#define PORT 8080 
int main(int argc, char const *argv[]) 
{ 
    int server_fd, new_socket, valread; 
    struct sockaddr_in address; 
    int opt = 1; 
    int addrlen = sizeof(address); 
    char buffer[250] = {0}; 
    char *hello = "Hello from server";
	char *msg; 
    vector <int> whitelist;

    printf("W7OSU Door Lock Network Authentication Server\nCreated by Drake Vidkjer on 6/4/18\nModified by Carson Downer on 1/23/2020\nSystem Starting...\n");

    // Creating socket file descriptor 
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) 
    { 
        perror("socket failed"); 
        exit(EXIT_FAILURE); 
    } 
       
    // Forcefully attaching socket to the port 8080 
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, 
                                                  &opt, sizeof(opt))) 
    { 
        perror("setsockopt"); 
        exit(EXIT_FAILURE); 
    } 
    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = INADDR_ANY; 
    address.sin_port = htons( PORT ); 
    printf("Waiting for client\n");
    // Forcefully attaching socket to the port 8080 
    if (bind(server_fd, (struct sockaddr *)&address,  
                                 sizeof(address))<0) 
    { 
        perror("bind failed"); 
        exit(EXIT_FAILURE); 
    } 
    if (listen(server_fd, 3) < 0) 
    { 
        perror("listen"); 
        exit(EXIT_FAILURE); 
    } 
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address,  
                       (socklen_t*)&addrlen))<0) 
    { 
        perror("accept"); 
        exit(EXIT_FAILURE); 
    } 

    //send(new_socket , hello , strlen(hello) , 0 ); 
    printf("Connection estabolished\n"); 

    printf("Getting Whitelist\n");
	
	//read the whitelist into the program
	getWhitelist(whitelist);
	
	//print the whielist
    int i = 0;
	printf("Whitelist:\n");
	for(i = 0; i < whitelist.size(); i++)
	{
		printf("%d\n", whitelist[i]);
	}
    while(1){
        valread = read( new_socket , buffer, 1024); 
        printf("%s\n",buffer ); 
        if(testKey(whitelist,getKeyID(buffer))){
			buffer[21] = '1';
        printf("Authorized Key");
            storeEncryption(buffer);
        }

		send(new_socket , buffer , strlen(buffer) , 0 );
    }

    return 0; 
} 

void getWhitelist(vector <int> &whitelist)
{
	//define local variables
	char timeString[24];
	char inString[10];
	int i = 0;
	
	//read in the whitelist 
	ifstream whiteRead("whitelist.txt");
	if(whiteRead.is_open())
	{
		//read each line of the whitelist file as another number in the whitelist
		while(!whiteRead.eof())
		{
			whiteRead.getline(inString, 10);
			whitelist.push_back(atoi(inString));
			i++;
		}
		
		printf("Whitelist size: %ld\n", whitelist.size());
		
		//then close the file
		whiteRead.close();
	}
	else
	{
		//open up log file
		ofstream sysLog("sys_log.txt", ios::app);
		
		//display messgaes
		printf("Unable to find whitelist file \"whitelist.txt\", please make sure the file is in the program directory.\n");
		getTime(timeString);
		sysLog << timeString << "Unable to find whitelist file \"whitelist.txt\", please make sure the file is in the program directory.\n\n";
		
		//close log file and exit the program
		sysLog.close();
		exit (EXIT_FAILURE);
	}
	return;	
}


bool testKey(vector <int> &whitelist, int keyTry)
{
	uint8_t i = 0;

	//traverse the array to try and find the string
	for(i = 0; i < whitelist.size(); i++)
	{
		if(whitelist[i] == keyTry)
		{
		  return true;
		}
	}
	return false;
}

void getTime(char* timeString)
{
	time_t rawTime = time(NULL);
	struct tm * timeInfo = localtime(&rawTime);
	strcpy(timeString, asctime(timeInfo));
	strtok(timeString, "\n");
	
	return;
}

void accLog(bool pass, int keyTry, sockaddr_in &clientIP)
{
	char timeString[30];
	
	//open up log file
	ofstream accLog("acc_log.txt", ios::app);
	
	//get the current time
	getTime(timeString);
	
	//write the message to the file
	printf("%s Request from IP: %s For user: %d Pass(1)/Failed(0): %d\n", timeString, inet_ntoa(clientIP.sin_addr), keyTry, pass);
	accLog << timeString << " Request from IP: " << inet_ntoa(clientIP.sin_addr) << "For user: " << keyTry << "Pass(1)/Failed(0): " << pass << "\n";
	
	//close log file and exit the program
	accLog.close();
}

int getKeyID(char* data){
	string hex;
	int ID, i;
	for(i = 0; i < strlen(data); i++){
		if(i > 11 && i < 20)
			hex+=data[i];
	}

	ID = hexToDec(hex);
	return ID;
}

void storeEncryption(char* data){
	char timestring[30];
	uint8_t i;
	vector <int> whitelist;
	string lockID, time, Key, action;
	for(i = 0; i < strlen(data) ;i++){
        if(i < 4)
            lockID += data[i];

        if(i > 3 && i < 12)
            time += data[i];

        if(i > 11 && i < 20)
            Key += data[i];

        if(i > 19)
            action += data[i];
    }
	ofstream ClientLog("clientlog.txt", ios::app);
	lockID = "Lock ID: "+to_string(hexToDec(lockID.c_str()));
	// time = ", Time: " +to_string(hexToDec(time));
	Key = ", Key: "+to_string(hexToDec(Key));
	getWhitelist(whitelist);
	getTime(timestring);
	ClientLog << timestring + " " + lockID+ Key + action << endl;
	ClientLog.close();
}


int hexToDec(string hexa){
	char* hex = const_cast<char*>(hexa.c_str());
	int dec = 0;
	int length = 0;
	char*hexstr;
	for (hexstr = hex; *hexstr != '\0'; hexstr++) {
      length++;
    }
	int i;
	hexstr = hex;
	for (i = 0; *hexstr != '\0' && i < length; i++, hexstr++) {
      // Compare *hexstr with ASCII values
      if (*hexstr >= 48 && *hexstr <= 57) {  // is *hexstr Between 0-9
          dec += (((int)(*hexstr)) - 48) * pow(16, length - i - 1);
        }
      else if ((*hexstr >= 65 && *hexstr <= 70))  {  // is *hexstr Between A-F
          dec += (((int)(*hexstr)) - 55) * pow(16, length - i - 1);
        }
      else{  // is *hexstr Between a-f
          dec += (((int)(*hexstr)) - 87) * pow(16, length - i - 1);
        }

	}
	return dec;
 
}
