 /*
  * (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.
  */
#include "main.h"

void usage(char *str)
{
	fprintf(stderr,"usage: %s <MyIP:port> -j <HelperIP:port>\n",str);
	exit(-1);
}


void InitSocket()
{
	struct sockaddr_in my_addr;
	struct location address = GetMyLocation();
	
	bzero((char *)&my_addr, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_addr.s_addr = address.ip.s_addr;
	my_addr.sin_port = htons(address.port);
	
	chord_socket = socket(AF_INET, SOCK_DGRAM, 0);
	if(chord_socket <= 0)
	{
		perror("socket error");
		exit(-7);
	}
	printf("Trying to bind socket=%d, ip=%d, port=%d\n",chord_socket, address.ip.s_addr, address.port);

	if(bind(chord_socket, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0)
	{
		perror("bind error");
		exit(-8);
	}
}

void InitSemaphores()
{
	sem_init(&table_sem, 0, 1);
	sem_init(&hash_sem, 0, 1);
	sem_init(&mutex_sem, 0, 1);
}

void Initialize(int myID, int helper_id)
{
	mytable = new MyTable(myID, helper_id);
	
	InitSocket();

	pthread_create(&recv_thread, NULL, RecvThread, NULL);
	pthread_create(&sos_thread, NULL, SOS_Helper, NULL);
}


void StabilizeSuccessor()
{
	UINT myID = GetMyID();
	UINT newSucc;
	UINT mySucc;
	while(TRUE)
	{
		mySucc = GetMySuccessor();
		if(mySucc != myID)
		{
			newSucc = Remote_AskPredecessor(mySucc);
		}
		else
			newSucc = GetMyPredecessor();
		if(newSucc == UINT_ERROR)
		{
			printf("Remote_AskPredecessor Failed\n");
			ResetStabilizeTimer();
			if(SetNextSuccessor(mySucc))
			{
				mySucc = GetMySuccessor();
				Remote_Notify(mySucc);
			}
			else
				break;
		}
		else
		{
			if(Belongs(newSucc, myID, mySucc, 0, 0, TRUE))
			{
				ResetStabilizeTimer();
				SetFinger(0, newSucc);
			}
			break;
		}
	}
	mySucc = GetMySuccessor();
	Remote_Notify(mySucc);
}

void FindSuccessor(UINT start)
{
	UINT myID = GetMyID();
	UINT mySucc = GetMySuccessor();
	if(Belongs(start, GetIthStart(0), mySucc, 1, 1, FALSE))
	{
		int index = GetFingerIndex(start);
		SetFinger(index, mySucc);
	}
	else
	{
		UINT nextNode = GetClosestPrecedingFinger(start);
		if(nextNode == myID)
			fprintf(stderr,"ERROR: Finger Table bad\n");
		struct Query q = MakeStandardQuery(nextNode, nextNode, QueryType_FindSuccessor);
		q.QueryData.id = start;
		SendQueryData(q);
	}
}

void FixFingers()
{
	UINT myID = GetMyID();
	UINT mySucc = GetMySuccessor();
	if(myID == mySucc)
		return;
	for(int i=1;i < LOGN;i++)
	{
		// This should be non-blocking....
		FindSuccessor(GetIthStart(i));
	}
}

void Stabilize()
{
	// Stabilizing thread...
	while(TRUE)
	{
		PrintFingerTable(CURR_STABILIZE_TIMER);
		CURR_STABILIZE_TIMER *= 2;
		CURR_STABILIZE_TIMER = (CURR_STABILIZE_TIMER > MAX_TIMER)?(MAX_TIMER):(CURR_STABILIZE_TIMER);

		StabilizeSuccessor();
		FixFingers();
		usleep(CURR_STABILIZE_TIMER);
	}
}

void JoinNetwork(UINT helper_id)
{
//	UINT myID = GetMyID();
//	FindSuccessor(myID);
	UINT mySucc = GetMySuccessor();
	Remote_Notify(mySucc);
}

main(int argc, char *argv[])
{
	if(argc < 4)
		usage(argv[0]);

	hashtable = new HashTable();

	InitSemaphores();

//	printf("Lets Parse the locations\n");
	UINT myID = ParseAndAddLocation(argv[1]);
	if(strcmp(argv[2],"-j") != 0)
		usage(argv[0]);
	UINT helper_id = ParseAndAddLocation(argv[3]);

	// Initialize the data structures and start the recieving thread..
	Initialize(myID, helper_id);

	// Lets Join the network
	if(myID != helper_id)
		JoinNetwork(helper_id);
	
	// Turn into a stabilizing routine...
	Stabilize();
}
