 /*
  * (C) 2003-2004 Columbia University Departments of Computer Science and
  * Electrical Engineering
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, is permitted provided the original authors give
  * written consent and the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above
  *    copyright notice, this list of conditions and the following
  *    disclaimer in the documentation and/or other materials
  *    provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  */

int HashTable::hash(UINT id)
{
	return (id+MAX_HASH)%MAX_HASH;
}

void HashTable::Update(UINT id, struct in_addr ip, in_port_t port)
{
	int index = hash(id);
	
	struct mapping *tmp;
//	printf("In Update id=%d, ip=%d, port=%d, index=%d\n",id, ip, port, index);
//	printf("In Update id=%d, ip=%s, port=%d, index=%d\n",id, inet_ntoa(ip), port, index);
	for(tmp = Hash[index];tmp != NULL; tmp = tmp->next)
	{
		if(tmp->id == id)
			break;
	}

	if(tmp == NULL)
	{
		// New Entry
		tmp = Hash[index];
		Hash[index] = (struct mapping *)malloc(sizeof(struct mapping));
		Hash[index]->id = id;
		Hash[index]->sin_addr = ip;
		Hash[index]->sin_port = port;
		Hash[index]->next = tmp;
	}
	else
	{
		// Entry already exists
		tmp->sin_addr = ip;
		tmp->sin_port = port;
	}
}

void HashTable::Delete(UINT id)
{
	int index = hash(id);
	struct mapping *tmp, *prev;

	for(prev = tmp = Hash[index];tmp != NULL;tmp = tmp->next)
	{
		if(tmp->id == id)
			break;
		prev = tmp;
	}

	if(tmp != NULL)
	{
		if(tmp == Hash[index])
			Hash[index] = tmp->next;
		else
		{
			prev->next = tmp->next;
		}
	}
}

UINT HashTable::FindLowestNode()
{
	UINT myID = GetMyID();
	
	int curr_min = MAX_N;
	UINT curr_lowest = myID;
	struct mapping *tmp;
	for(int i=0;i<MAX_HASH;i++)
	{
		for(tmp = Hash[i]; tmp != NULL;tmp = tmp->next)
		{
			int new_min = (tmp->id + MAX_N - myID)%MAX_N;
			if((new_min < curr_min)&&(new_min > 0))
			{
				curr_min = new_min;
				curr_lowest = tmp->id;
			}
		}
	}
	return curr_lowest;
}

void HashTable::Update(struct location loc)
{
	Update(loc.id, loc.ip, loc.port);
}

struct in_addr HashTable::strTOip(char *IPStr)
{
	struct in_addr ip;
	inet_aton(IPStr, &ip);
	return ip;
}

in_port_t HashTable::strTOport(char *PortStr)
{
	in_port_t port= atoi(PortStr);
//	printf("%s: string=<%s>, port=%d\n",__FUNCTION__,PortStr,port);
	return port;
}

UINT HashTable::ParseAndAddLocation(char *str)
{
	// The string is of the form IP:port
	// Add this location in the hashtable and return the ID

//	printf("Parsing String <%s>\n",str);
	char *colon = strchr(str,':');
	if(colon == NULL)
		return UINT_ERROR;
	
	colon[0] = 0;
	colon++;

	// Get IP
	struct in_addr ip = strTOip(str);
	// Get port
	in_port_t port = strTOport(colon);
	// Get chordID
	UINT id = ChordHash(ip, port);

	// Add to Hash Table
	Update(id, ip, port);

	// Return ID
	return id;
}

struct location HashTable::GetLocation(UINT id)
{
//	printf("GetLocation called for ID %d\n",id);
	int index = hash(id);
	struct mapping *tmp;
	struct location loc;
	for(tmp = Hash[index];tmp != NULL; tmp = tmp->next)
	{
		if(tmp->id == id)
			break;
	}

	if(tmp == NULL)
	{
//		printf("Error: Location not found for id = %d\n",id);
		// Error: entry for id not found
		loc.id = UINT_ERROR;
	}
	else
	{
		loc.id = id;
		loc.ip =  tmp->sin_addr;
		loc.port = tmp->sin_port;
//		printf("And I found id=%d, ip=%s, port=%d\n",id,inet_ntoa(loc.ip),loc.port);
	}
	return loc;
}

void HashTable::GetIPFromID(UINT id, char resultIP[])
{
	for(int i=0;i < MAX_HASH;i++)
	{
		for(struct mapping *tmp=Hash[i];tmp != NULL;tmp = tmp->next)
		{
			if(tmp->id == id)
			{
//	printf("In %s id=%d, ip=%s, port=%d\n",__FUNCTION__, id, inet_ntoa(tmp->sin_addr), tmp->sin_port);
				strcpy(resultIP, inet_ntoa(tmp->sin_addr));
				return;
			}
		}
	}
}

UINT HashTable::GetIDFromIP(char inputIP[])
{
	struct in_addr IPaddr;
	inet_aton(inputIP, &IPaddr);
	for(int i=0;i < MAX_HASH;i++)
	{
		for(struct mapping *tmp=Hash[i];tmp != NULL;tmp = tmp->next)
		{
			if(tmp->sin_addr.s_addr == IPaddr.s_addr)
				return tmp->id;
		}
	}
	return UINT_ERROR;
}

