



#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/timeb.h>
#define WIN32_LEAN_AND_MEAN 
#include <winsock2.h> 
#include <time.h>
#include "math.h"
#include "datatypes.h"

#include "helpers.h"


int CheckIfInList(struct HostID HID, std::list<struct HostID> &List)
{
	// returns 1 if HID is in List
	std::list<struct HostID>::iterator it;
	for (it=List.begin(); it!=List.end(); ++it)
		if (strcmp(it->IP,HID.IP)==0 && it->Port == HID.Port)
			return 1;
	return 0;
}
	



struct HostID SelectNeighbor(std::list<struct HostID> &ActiveNeighbors, std::list<struct HostID> &SemiActiveNeighbors, std::list<struct HostID> &AllHosts, struct HostID &ThisHost)
{
// pick a host randomly from list of all hosts. If the selected host is already active or semi-active, then pick again

	int cnt=0;
	while (1)
	{
		if (cnt++ > 10000)
			break;
		int HostToPick = floor((double)rand()/(double)RAND_MAX*AllHosts.size()); // pick a random number from 0 to the size of AllHosts
		if (HostToPick == AllHosts.size())
			HostToPick--;	// just in case

		std::list<struct HostID>::iterator it;
		for (it=AllHosts.begin(); it!=AllHosts.end(); ++it)
		{
			if (HostToPick==0)
			{
				if (CheckIfInList(*it,ActiveNeighbors)==0 && CheckIfInList(*it,SemiActiveNeighbors)==0 && (strcmp(it->IP,ThisHost.IP)!=0||it->Port!=ThisHost.Port) )				
					return *it;
				else
					break; // select another
			}
			HostToPick--;
		}
	}	
	printf("!!!!!Failed to select from the list of all hosts that is not either active or semi-active. \n");
	return AllHosts.front(); // this should never happen
}

	
void CheckIfNeighborsHaveSentHello(std::list<struct HostID> &Neighbors)
{
	// this checks if a neighbors has sent a hello within the past 40 seconds or not
	std::list<struct HostID>::iterator it;
	std::list<struct HostID>::iterator LastIt;
	struct timeb TimeBuffer;
	ftime( &TimeBuffer ); 

	it=Neighbors.begin(); 
	while (it!=Neighbors.end())
	{
		int Del=0;
		if (TimeBuffer.time - it->LastHelloRec > 40)
			Del=1;	
		LastIt = it;
		++it;
		if (Del==1)
			Neighbors.erase(LastIt);
	}
}
	




void PrintPacketRecieved(struct PktStruct *pkt, struct HostID ThisHost)
{
	// When the host has received a packet, this can be used to print the contents of the packet

	printf("GotPacket: %s:%d ----> %s:%d(me)\n",pkt->SenderIP,pkt->SenderPort,ThisHost.IP,ThisHost.Port);

	switch (pkt->Type)
	{
	case HELLO_PKT_TYPE: 
		if (pkt->NumberOfRecentlyHeardNeighbors>0)
		{
			printf("advertising \n");		
			for (int i=0; i<pkt->NumberOfRecentlyHeardNeighbors; i++)
			{
				printf("	%s:%d\n",pkt->RecentNeighbors[i].IP,pkt->RecentNeighbors[i].Port);
			}
		}
		printf("\n");
		printf("Received routing table\n");
		printf("       Dest              Next Hop             Cost\n");
		printf("-----------------------------------------------------\n");
		for (n=0; n<pkt->NumberOfEntriesInRoutingTable; n++)
		{
			printf("%s:%d     %s:%d      %d\n",pkt->RoutingTable[n].DestIP,pkt->RoutingTable[n].DestPort,pkt->RoutingTable[n].NextHopIP,pkt->RoutingTable[n].NextHopPort,pkt->RoutingTable[n].Cost);
		}

		break;
	default: printf("unknown packet type\n");
	}
}


void PrintPacketTransmitted(struct PktStruct *pkt, struct HostID Dest)
{
	// When the host is about to send a packet, this can be used to print the contents of the packet to be sent
	printf("Sending packet %s:%d(me) --> %s:%d\n",pkt->SenderIP,pkt->SenderPort,Dest.IP,Dest.Port);

	switch (pkt->Type)
	{
	case HELLO_PKT_TYPE: 
		if (pkt->NumberOfRecentlyHeardNeighbors>0)
		{
			printf("advertising \n");		
			for (int i=0; i<pkt->NumberOfRecentlyHeardNeighbors; i++)
			{
				printf("	%s:%d\n",pkt->RecentNeighbors[i].IP,pkt->RecentNeighbors[i].Port);
			}
		}
		printf("\n");
		printf("Sending routing table\n");
		printf("       Dest              Next Hop             Cost\n");
		printf("-----------------------------------------------------\n");
		for (n=0; n<pkt->NumberOfEntriesInRoutingTable; n++)
		{
			printf("%s:%d     %s:%d      %d\n",pkt->RoutingTable[n].DestIP,pkt->RoutingTable[n].DestPort,pkt->RoutingTable[n].NextHopIP,pkt->RoutingTable[n].NextHopPort,pkt->RoutingTable[n].Cost);
		}

		break;
	default: printf("unknown packet type\n");
	}
}



// slightly changed for distance vector
void PrintStatus(std::list<struct HostID> &ActiveNeighbors, std::list<struct HostID> &SemiActiveNeighbors, struct HostID TempNeighbor, int SearchingForNeighborFlag, int TimeBetweenPrints, struct HostID &ThisHost)
{	
	// This prints the current status
	// With TimeBetweenPrints, the time between prints can be controlled. Specifically, the status will not be printed out more frequenctly than TimeBetweenPrints

	static int LastTimePrinted = 0;
	struct timeb TimeBuffer;
	ftime( &TimeBuffer ); 

	if (TimeBuffer.time - LastTimePrinted > TimeBetweenPrints)
	{
		printf("\n\n\nThere are %d neighbors and %d are required\n",ActiveNeighbors.size()+SemiActiveNeighbors.size(),DESIRE_NUMBER_OF_NEIGHBORS);
		if (SearchingForNeighborFlag==1)
			printf("Searching for a new neighbor: %s:%d\n",TempNeighbor.IP,TempNeighbor.Port);

		LastTimePrinted = TimeBuffer.time;
		
		
		std::list<struct HostID>::iterator Ait;
		Ait=ActiveNeighbors.begin(); 
		std::list<struct HostID>::iterator Sit;
		Sit=SemiActiveNeighbors.begin(); 
		
		printf("Active Neighbors   Time        SemiActive     Time \n");
		printf("----------------------------------------------------\n");
		
		while (1)
		{
			if (Ait!=ActiveNeighbors.end() && Sit!=SemiActiveNeighbors.end())
			{			
				printf("%s:%d    %d			%s:%d	%d\n",Ait->IP,Ait->Port, TimeBuffer.time-Ait->LastHelloRec, Sit->IP, Sit->Port, TimeBuffer.time-Sit->LastHelloRec);
				++Ait; ++Sit;
			}
			else 
			{
				if (Ait!=ActiveNeighbors.end())
				{					
					printf("%s:%d    %d			\n",Ait->IP,Ait->Port, TimeBuffer.time-Ait->LastHelloRec);
					++Ait;
				}
				else 
				{	
					if (Sit!=SemiActiveNeighbors.end())
					{	
						printf("                              			%s:%d	%d\n",Sit->IP, Sit->Port, TimeBuffer.time-Sit->LastHelloRec);
						++Sit;
					}
					else
						break;
				}
			}
		}
		printf("-------------------------------------------------------\n\n");
		ShowRoutingTable(ThisHost);

	}	

}




void FillThisHostIP(struct HostID &ThisHost)
{
	// This determines the host IP address and saves if in ThisHost.IP
	char MyIP[80];
	struct hostent * hp; 
	struct sockaddr_in to;

	gethostname(MyIP,2000);
	hp = gethostbyname(MyIP); 
	memcpy(&(to.sin_addr),hp->h_addr,hp->h_length); 
	strcpy(ThisHost.IP,inet_ntoa(to.sin_addr));	

}
		


void ReadAllHostsList(char *fn, std::list<struct HostID> &AllHosts)
{
	// This reads the hosts IDs (IP and port) listed in file fn and puts the host IDs in AllHosts
	// This function should be called during initialization
	FILE *fptr;
	fptr = fopen(fn,"rt");
	if (fptr==NULL)
	{
		printf("error: could not open file %s with list of all hosts\n",fn);
		exit(0);
	}

	char str[20];
	int port;
	printf("possible neighbors: \n");
	while (fscanf(fptr,"%s %d",str, &port)!=EOF )
	{
		printf("	%s %d\n",str,port);
		struct HostID hid;
		strcpy(hid.IP,str);
		hid.Port=port;
		AllHosts.push_back(hid);
	}
	printf("\n");
	fclose(fptr);
}

// new for distance vector

void ShowRoutingTable(struct HostID &ThisHost)
{
	printf("\n\n");
	printf("table size=%d\n",ThisHost.NumberOfEntriesInRoutingTable);
	printf("       Dest              Next Hop             Cost\n");
	printf("-----------------------------------------------------\n");
	for (int m=0; m<ThisHost.NumberOfEntriesInRoutingTable; m++)
	{
		printf("%s:%d      %s:%d       %d\n",ThisHost.RoutingTable[m].DestIP,ThisHost.RoutingTable[m].DestPort,ThisHost.RoutingTable[m].NextHopIP,ThisHost.RoutingTable[m].NextHopPort,ThisHost.RoutingTable[m].Cost);
	}
	printf("\n\n");
}
