// Global Variables
RING_UPPER_LIMIT = 20;   // when node joining or leaving, if # of nodes > UPPER_LIMIT, ring-formation
RING_LOWER_LIMIT = 10;   // when node joining or leaving, if # of nodes < LOWER_LIMIT, ring-destruction
HEARTBEAT_TIMEOUT = 5;   // timeout set for liveness checking
HEARTBEAT_INTERVAL = 5;  // interval set between two heartbeat messages
NUM_CONTACT_NODES = 3;   // number of contact nodes elected in each ring

================================================================================================================
/* Structure of a Node
 *  - level (int, the level of ring this node is in, not useful?)
 *  - has_subring (true or false)
 *  - role ("Residence, Relay")
 *  - curr_contact_nodes[] (the contact nodes of this ring)
 *  - upper_contact_nodes[] (the contact nodes of the upper level ring)
 *  - predecessor (the one behind this node on the ring)
 *  - successor (the one next to this node on the ring)
 *  - pre-predecessor (used for checking liveness of its predecessor)
 *  - suc-successor (used for checking liveness of its successor)
 *  - sequence_number (int, deferenciate which broadcast, for each broadcast there will be a new contact node or a few contact nodes)
 *  - broadcast_key (int, k-nary broadcast, randomly generated by the contact node (2, 3, 4, 5))
 */
================================================================================================================

================================================================================================================
/* Node Join
 * 1. ping contact node of level 1 ring, it will leads you to the closest ring's contact node(s)
 * 2. recursively find the contact nodes of the ring which does not have a subring
 * 3. add to the end of the ring (by end we mean the one next to the node with id 0)
 * 4. broadcast to the other nodes within the ring
 */
node_join() {
	contact_node = find_the_ring(starter.ip)  // recursively find the contact node of the ring to join (starter is the contact node of the largest ring)
	initialize_node()
	in_ring_broadcast()
}
================================================================================================================

================================================================================================================
// Node Left (Detected by liveness check)
// Liveness check: A node will send heartbeat message to its predecessor and successor periodically
detect_node_left() {
	if (liveness_check_predecessor(this.predecessor, this.pre-predecessor) == false)
		in_ring_broadcast()  // broadcast to update node info within the smallest ring
	if (liveness_check_successor(this.successor, this.suc-successor) == false)
		in_ring_broadcast()  // broadcast to update node info within the smallest ring
}

liveness_check_predecessor(predecessor, pre-predecessor) {
	return_obj = ping(predecessor.ip)
	if (return_obj.msg == 'TIMEOUT') {
		// check with its pre-predecessor (Remote-Procedure-Call)
		status = check_your_successor(pre-predecessor.ip)
		if (status == false) {
			// recheck
			return_obj = ping(predecessor.ip)
			if (return_obj.msg == 'TIMEOUT')
				return false;  // timeout
			else
				return true;   // alive
		}
		else
			return true;   // alive
	}
}

liveness_check_successor(successor, suc-successor) {
	return_obj = ping(successor.ip)
	if (return_obj.msg == 'TIMEOUT') {
		// check with its suc-successor (Remote-Procedure-Call)
		status = check_your_predecessor(suc-successor.ip)
		if (status == false) {
			// recheck
			return_obj = ping(successor.ip)
			if (return_obj.msg == 'TIMEOUT')
				return false;  // timeout
			else
				return true;   // alive
		}
		else
			return true;   // alive
	}
}

// remote procedure call executed by its pre-predecessor
check_your_predecessor() {
	return_obj = ping(this.predecessor.ip)
	if (return_obj.msg == 'TIMEOUT')
		return false;
	else
		return true;
}

// remote procedure call executed by its suc-successor
check_your_sucessor() {
	return_obj = ping(this.successor.ip)
	if (return_obj.msg == 'TIMEOUT')
		return false;
	else
		return true;
}
================================================================================================================

================================================================================================================
// broadcast within a ring (the smallest ring)
in_ring_broadcast() {
	int k = random(2, 3, 4, 5)  // generate a random number using SGX
	root = form_spanning_tree()
	broadcast_from_the_root(root)
}

// broadcast from the root
broadcast_from_the_root(root) {
	// DFS or BFS
}
================================================================================================================

================================================================================================================
// broadcast to the whole network
broadcast() {
	contact_node = find_the_contact_node(this.contact_node)  // start from this smallest ring, recursively find the contact node of the largest ring
	broadcast_from_the_contact_node(contact_node)
}

broadcast_from_the_contact_node(contact_node) {
	int k = random(2, 3, 4, 5)  // generate a random number using SGX
	root = contact_node.form_spanning_tree()
	recursive_broadcast(root)
}

recursive_broadcast(root) {
	if (root == null)
		return;
	if (root.has_subring)
		broadcast_from_the_contact_node(root)
	else
		notify_current_node(root)
	for each child:
		if (this_child.has_subring)
			broadcast_from_the_contact_node(this_child)
		else
			notify_current_node(this_child)
}
================================================================================================================

================================================================================================================
// contact node(s) election and notify all nodes within a ring
election() {
	random_IDs = []
	for i = 0 -> NUM_CONTACT_NODES:
		random_IDs.append(generate_random_number_using_SGX() mod num_nodes)
	broadcast_within_ring(random_IDs)
	multicast_upper_ring(random_IDs)
}
================================================================================================================