/*
 * Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <glib.h>
#include <sys/time.h>
#include <ctype.h>
#include <strings.h>
#include <time.h>
#include <fcntl.h>
#include <uuid/uuid.h>
#include <string.h>

#include "pathComp_log.h"
#include "pathComp_tools.h"
#include "pathComp_cjson.h"
#include "pathComp_ksp.h"
#include "pathComp_sp.h"
#include "pathComp_ear.h"
#include "pathComp_RESTapi.h"

#define ISspace(x) isspace((int)(x))

#define SERVER_STRING "Server: PATHCOMP/0.1.0\r\n"

// List of Clients connected to the PATH COMP
GList *RESTapi_tcp_client_list = NULL;

// Id for CLient HTTP (REST API) Connection
guint CLIENT_ID = 0;
guint32 paId_req = 0;

// Global variables
GList* linkList;
GList* deviceList;
GList* serviceList;
GList* activeServList;

gchar algId[MAX_ALG_ID_LENGTH];
gboolean syncPath = FALSE;

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Seek a connected tcp client by its fd in the RESTapi_tcp_client_list
 * 	
 * 	@param data
 *  @param userdata
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint find_rl_client_by_fd (gconstpointer data, gconstpointer userdata)
{
	 /** check values */
     g_assert(data != NULL);
     g_assert(userdata != NULL);
	 
	 struct pathComp_client *client = (struct pathComp_client*)data;
     gint fd = *(gint *)userdata; 
     
	 if (client->fd == fd) {
		 return 0;
	 }
    return -1;	
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to send a message to the corresponding channel
 * 	
 * 	@param source
 *  @param buf
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint rapi_send_message (GIOChannel *channel, char *buf)	{
	gsize nbytes, buffersize;

	//DEBUG_PC ("Msg prepared to be sent REST API RESPONSE ");
    gint len = strlen ((const char*) buf);
    //DEBUG_PC ("Targeted Length of the buffer: %d", len);       

    buffersize = g_io_channel_get_buffer_size (channel);
    //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
    
    gsize newBufferSize = MAX_GIO_CHANNEL_BUFFER_SIZE;
    g_io_channel_set_buffer_size (channel, newBufferSize);
    
    buffersize = g_io_channel_get_buffer_size (channel);
    //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
    
	/** Send message.  */
    GError *error = NULL;
    
    char *ptr = buf;
    gint nleft = strlen ((const char *)buf);
    
    while (nleft > 0) {
        g_io_channel_write_chars (channel, (void *)ptr, nleft, &nbytes, &error);
        if (error) {
            DEBUG_PC ("Error sending the message to TCP Client");
            return (-1);
        }
        
        //DEBUG_PC ("Sent %d bytes", (gint)nbytes);        
        nleft = nleft - nbytes;
        //DEBUG_PC ("Remaining to be sent %d", nleft);
        ptr += nbytes;        
    } 
	DEBUG_PC("RESPONSE MSG SENT");
	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to return when something goes wrong when processing the REST API Command
 * 	
 * 	@param source
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_unimplemented (GIOChannel *source)
{
	gint ret = 0;
	guchar buftmp[1024];
	char *buf = g_malloc0 (sizeof (char) * 2048000); 
	sprintf((char *)buf, "HTTP/1.1 400 Bad request\r\n");

	sprintf((char *)buftmp, SERVER_STRING);
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "Content-Type: text/plain\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "\r\n");
	strcat ((char *)buf, (const char *)buftmp);
	      
	sprintf((char *)buftmp, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "</TITLE></HEAD>\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	sprintf((char *)buftmp, "<BODY><P>HTTP request method not supported.\r\n");
	strcat ((char *)buf, (const char *)buftmp);	

	sprintf((char *)buftmp, "</BODY></HTML>\r\n");
	strcat ((char *)buf, (const char *)buftmp);

	ret = rapi_send_message (source, buf);
	g_free (buf);
	(void)ret;

	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to put in the buffer the date according to RFC 1123
 * 	
 * 	@param date
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_add_date_header (char *date)
{
    static const char *DAYS[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    static const char *MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

    time_t t = time(NULL);
    struct tm *tm = NULL;
    struct tm sys;
    gmtime_r(&t, &sys);
    tm = &sys;

    sprintf((char *)date, "DATE: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", DAYS[tm->tm_wday], tm->tm_mday, 
								      MONTHS[tm->tm_mon], 1900 + tm->tm_year, 
								      tm->tm_hour, tm->tm_min, tm->tm_sec);
    
    return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to add DeviceId and EndpointId forming the computed path
 *
 * 	@param pathObj
 *  @param p
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void add_comp_path_deviceId_endpointId_json(cJSON* pathObj, struct path_t* p, struct compRouteOutput_t* oElem) {
	g_assert(p);
	g_assert(pathObj);

	// add array for the devideId and endpointIds
	cJSON* devicesArray = cJSON_CreateArray();
	cJSON_AddItemToObject(pathObj, "devices", devicesArray);

	// Add the source endpoint
	cJSON* sEndPointIdObj;
	cJSON_AddItemToArray(devicesArray, sEndPointIdObj = cJSON_CreateObject());
	// Add the topology Id Object containing the topology uuid and context uuid
	cJSON* stopIdObj;
	cJSON_AddItemToObject(sEndPointIdObj, "topology_id", stopIdObj = cJSON_CreateObject());
	cJSON_AddItemToObject(stopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.contextId));
	cJSON_AddItemToObject(stopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.topology_uuid));

	// Add the device Id (uuid) 
	cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[0].device_uuid));
	// Add the endpoint Id (uuid)
	cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].endpoint_uuid));


	for (gint i = 0; i < p->numPathLinks; i++) {
		struct pathLink_t* pL = &(p->pathLinks[i]);
		cJSON* dElemObj; // Device Element Object of the array
		cJSON_AddItemToArray(devicesArray, dElemObj = cJSON_CreateObject());

		// Add the topologyId with the topologyuuid and contextId
		cJSON* tIdObj;
		cJSON_AddItemToObject(dElemObj, "topology_id", tIdObj = cJSON_CreateObject());
		cJSON_AddItemToObject(tIdObj, "contextId", cJSON_CreateString(pL->topologyId.contextId));
		cJSON_AddItemToObject(tIdObj, "topology_uuid", cJSON_CreateString(pL->topologyId.topology_uuid));

		// Add Device Id
		cJSON_AddItemToObject(dElemObj, "device_id", cJSON_CreateString(pL->aDeviceId));

		// Add endpoint Id
		cJSON_AddItemToObject(dElemObj, "endpoint_uuid", cJSON_CreateString(pL->aEndPointId));  
	}
	
	// Add the sink	endpoint
	cJSON* sinkEndPointIdObj;
	cJSON_AddItemToArray(devicesArray, sinkEndPointIdObj = cJSON_CreateObject());
	// Add the topology Id Object containing the topology uuid and context uuid
	cJSON* sinkTopIdObj;
	cJSON_AddItemToObject(sinkEndPointIdObj, "topology_id", sinkTopIdObj = cJSON_CreateObject());
	cJSON_AddItemToObject(sinkTopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.contextId));
	cJSON_AddItemToObject(sinkTopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.topology_uuid));

	// Add the device Id (uuid) 
	cJSON_AddItemToObject(sinkEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[1].device_uuid));
	// Add the endpoint Id (uuid)
	cJSON_AddItemToObject(sinkEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].endpoint_uuid));
	
	return;				 
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to add the links forming the computed path
 *
 * 	@param pathObj
 *  @param p
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void add_comp_path_link_json(cJSON* pathObj, struct path_t* p) {
	g_assert(p);
	g_assert(pathObj);

	// Add array for the links
	cJSON* linkArray = cJSON_CreateArray();
	cJSON_AddItemToObject(pathObj, "link", linkArray);

	for (gint i = 0; i < p->numPathLinks; i++) {
		struct pathLink_t* pL = &(p->pathLinks[i]);
		cJSON* lElemObj; // Link Element Object of the array
		cJSON_AddItemToArray(linkArray, lElemObj = cJSON_CreateObject());

		// Add link Id
		cJSON_AddItemToObject(lElemObj, "link_Id", cJSON_CreateString(pL->linkId));

		// Add link topologies
		cJSON* linkTopoArray = cJSON_CreateArray();
		cJSON_AddItemToObject(lElemObj, "topology", linkTopoArray);

		for (gint j = 0; j < pL->numLinkTopologies; j++) {
			struct linkTopology_t* linkTopo = &(pL->linkTopologies[j]);
			cJSON* lTopoElemObj;
			cJSON_AddItemToArray(linkTopoArray, lTopoElemObj = cJSON_CreateObject());
			cJSON_AddItemToObject(lTopoElemObj, "topology_uuid", cJSON_CreateString(linkTopo->topologyId));
		}
	}
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Compose the JSON Body of the succesfully network connectivity service
 * 	
 * 	@param body
 *  @param length
 *  @param compRouteOutputList
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_response_json_contents (char *body, gint *length, struct compRouteOutputList_t *compRouteOutputList)
{
    char *buftmp;    
    cJSON *root = cJSON_CreateObject();     
	DEBUG_PC ("Creating the JSON body of the response"); 

	// Create response-list array
	cJSON *responseListArray = cJSON_CreateArray();
	cJSON_AddItemToObject(root, "response-list", responseListArray);
	
	// Add computed routes to the response-list
	for (gint i = 0; i < compRouteOutputList->numCompRouteConnList; i++) {
		struct compRouteOutput_t *oElem = &(compRouteOutputList->compRouteConnection[i]); // reference to output element from the list of computed routes
		cJSON *oElemObj;
		cJSON_AddItemToArray (responseListArray, oElemObj = cJSON_CreateObject());
		
		// Add the service Id Object
		cJSON* servIdObj;
		cJSON_AddItemToObject(oElemObj, "serviceId", servIdObj = cJSON_CreateObject());
		cJSON_AddItemToObject(servIdObj, "contextId", cJSON_CreateString(oElem->serviceId.contextId));
		cJSON_AddItemToObject(servIdObj, "service_uuid", cJSON_CreateString(oElem->serviceId.service_uuid));

		// Add the service endpoints ids array
		cJSON* sEndpointIdsArray = cJSON_CreateArray();
		cJSON_AddItemToObject(oElemObj, "service_endpoints_ids", sEndpointIdsArray);

		for (gint j = 0; j < oElem->num_service_endpoints_id; j++) {
			//DEBUG_PC("parsing service endpoints ids");
			//DEBUG_PC("endpoint: %s [%s]", oElem->service_endpoints_id[j].device_uuid, oElem->service_endpoints_id[j].endpoint_uuid);
			//struct service_endpoints_id_t* sEndPointId = &(oElem->service_endpoints_id[j]);
			cJSON* sEndPointIdObj;
			cJSON_AddItemToArray(sEndpointIdsArray, sEndPointIdObj = cJSON_CreateObject());
			// Add the topology Id Object containing the topology uuid and context uuid
			cJSON* topIdObj;
			cJSON_AddItemToObject(sEndPointIdObj, "topology_id", topIdObj = cJSON_CreateObject());
			cJSON_AddItemToObject(topIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.contextId));
			cJSON_AddItemToObject(topIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.topology_uuid));

			// Add the device Id (uuid) 
			cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[j].device_uuid));
			// Add the endpoint Id (uuid)
			cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].endpoint_uuid));
		}
		// Add no path issue
		if (oElem->noPathIssue == NO_PATH_CONS_ISSUE) { // Error on finding the route, e.g., no feasible path
			DEBUG_PC("NO PATH FOUND, AN ISSUE OCCURRED: %d", oElem->noPathIssue);
			cJSON* noPathObj;
			cJSON_AddItemToObject(oElemObj, "noPath", noPathObj = cJSON_CreateObject());
			char str[5];
			sprintf(str, "%d", oElem->noPathIssue);
			cJSON_AddItemToObject(noPathObj, "issue", cJSON_CreateString(str));
			continue;
		}
		
		// Create the array to parse the computed path from the oElemObj
		cJSON* pathArray = cJSON_CreateArray();
		cJSON_AddItemToObject(oElemObj, "path", pathArray);
		for (gint k = 0; k < oElem->numPaths; k++) {
			struct path_t* p = &(oElem->paths[k]);
			cJSON* pathObj;
			cJSON_AddItemToArray(pathArray, pathObj = cJSON_CreateObject());

			// Add path capacity
			cJSON* pathCapObj;
			cJSON_AddItemToObject(pathObj, "path-capacity", pathCapObj = cJSON_CreateObject());
			cJSON* totalSizeObj;
			cJSON_AddItemToObject(pathCapObj, "total-size", totalSizeObj = cJSON_CreateObject());
			cJSON_AddItemToObject(totalSizeObj, "value", cJSON_CreateNumber(p->path_capacity.value));
			cJSON_AddItemToObject(totalSizeObj, "unit", cJSON_CreateNumber(p->path_capacity.unit));

			// Add path latency
			cJSON* pathLatObj;
			char lat[16];
			sprintf(lat, "%lf", p->path_latency.fixed_latency);
			cJSON_AddItemToObject(pathObj, "path-latency", pathLatObj= cJSON_CreateObject());
			cJSON_AddItemToObject(pathLatObj, "fixed-latency-characteristic", cJSON_CreateString(lat));

			// Add path cost
			cJSON* pathCostObj;
			cJSON_AddItemToObject(pathObj, "path-cost", pathCostObj = cJSON_CreateObject());
			cJSON_AddItemToObject(pathCostObj, "cost-name", cJSON_CreateString(p->path_cost.cost_name));
			char value[16];
			sprintf(value, "%lf", p->path_cost.cost_value);
			cJSON_AddItemToObject(pathCostObj, "cost-value", cJSON_CreateString(value));
			char algorithm[16];
			sprintf(algorithm, "%lf", p->path_cost.cost_algorithm);
			cJSON_AddItemToObject(pathCostObj, "cost-algorithm", cJSON_CreateString(algorithm));

			// Add the links
			//add_comp_path_link_json(pathObj, p);
			// Add deviceId, endpointId
			add_comp_path_deviceId_endpointId_json(pathObj, p, oElem);
		}	
	}
	
    //DEBUG_PC ("JSON Body Response DONE");	
    buftmp = (char *)cJSON_Print(root);
    strcat (body, (const char*) buftmp);
    *length = strlen ((const char*)body);    
    //DEBUG_PC ("JSON Body (length: %d)", *length);
    //DEBUG_PC ("%s", body);    
	cJSON_Delete (root);
	g_free(buftmp);
    return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to return response OK via REST API with the computed serviceId
 * 	
 * 	@param source
 *  @param httpCode
 *  @param compRouteOutputList
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_response_ok (GIOChannel *source, gint httpCode, struct compRouteOutputList_t *compRouteOutputList) {
	gint ret = 0;
    
    //DEBUG_PC ("Creating the JSON Body and sending the response of the computed Route List");
    
    char buftmp[1024];
    char *buf = g_malloc0 (sizeof (char) * 2048000); 
    // Create the Body of the Response 
    char * msgBody = g_malloc0 (sizeof (char) * 2048000);    
    gint length = 0;
	
	//  If path computation was requested, the resulting computation is returned in the msg body
	if (compRouteOutputList != NULL) {
		rapi_response_json_contents(msgBody, &length, compRouteOutputList);
	}
	// no path computation was requested, then a basic msg is just added
	else {
		cJSON* root = cJSON_CreateObject();
		char status[3];
		sprintf(status, "OK");
		cJSON_AddItemToObject(root, "Status", cJSON_CreateString(status));
		msgBody = (char*)cJSON_Print(root);
		length = strlen((const char*)msgBody);
		cJSON_Delete(root);
	}
		
	sprintf((char *)buf, "HTTP/1.1 200 OK\r\n");    
	
    sprintf((char *)buftmp, SERVER_STRING);
    strcat ((char *)buf, (const char *)buftmp);    
  
    sprintf ((char *)buftmp, "Content-Type: application/json\r\n");
    strcat ((char *)buf, (const char *)buftmp);    
    
    // Add the length of the JSON enconding to the Content_Length
    char buff_length[16];    
    sprintf(buff_length, "%d", length);
    
    sprintf ((char *)buftmp, "Content-Length: ");
    strcat ((char *)buftmp, (const char *)buff_length);
    strcat ((char *)buftmp, "\r\n");
    strcat ((char *)buf, (const char *)buftmp);    
    
    // Add DATE header
    rapi_add_date_header ((char *)buftmp);
    strcat ((char *)buf, (const char *)buftmp);     
    sprintf((char *)buftmp, "\r\n");
    strcat ((char *)buf, (const char *)buftmp);

	strcat((char*)buf, (const char*)msgBody);
			
	//DEBUG_PC ("%s", buf);	    
    ret = rapi_send_message (source, buf);    
    g_free (buf);
    memset (buftmp, '\0', sizeof ( buftmp));    
    g_free (msgBody);
    (void)ret;    
    return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to return response OK via REST API
 * 	
 * 	@param source
 *  @param error
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void rapi_response (GIOChannel *source, gint error) {
	 int ret = 0;	
	 guchar buftmp[1024];
	 char * buf = g_malloc0 (sizeof (char) * 2048000);
	 if (error == HTTP_RETURN_CODE_BAD_REQUEST)
		sprintf((char *)buf, "HTTP/1.1 400 BAD REQUEST\r\n");
	 else if (error == HTTP_RETURN_CODE_UNAUTHORIZED)
		sprintf((char *)buf, "HTTP/1.1 401 UNAUTHORIZED\r\n");
	 else if (error == HTTP_RETURN_CODE_FORBIDDEN)
		sprintf((char *)buf, "HTTP/1.1 403 FORBIDDEN\r\n");    
	 else if (error == HTTP_RETURN_CODE_NOT_FOUND)
		sprintf((char *)buf, "HTTP/1.1 404 NOT FOUND\r\n");
	 
	 sprintf((char *)buftmp, SERVER_STRING);
	 strcat ((char *)buf, (const char *)buftmp);    
	
	 sprintf((char *)buftmp, "Content-Type: text/plain\r\n");
	 strcat ((char *)buf, (const char *)buftmp);    
	 
	 sprintf((char *)buftmp, "Content-Length: 0/plain\r\n");
	 strcat ((char *)buf, (const char *)buftmp);    
	 
	 // Add DATE header
	 rapi_add_date_header ((char *)buftmp);
	 strcat ((char *)buf, (const char *)buftmp);     
	     
	 sprintf((char *)buftmp, "\r\n");
	 strcat ((char *)buf, (const char *)buftmp);
	 // Print the prepared message
	 DEBUG_PC ("%s", buf);
	 
	 // Send the message
	 ret = rapi_send_message (source, buf);	 
	 g_free (buf);	 
	 (void)ret;
	
	 return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief parsing topology Identifier Object (contains Context Id and Toplogy UUID) JSON object
 *
 * 	@param obj
 *  @param topology_id
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_topology_Id(cJSON* obj, struct topology_id_t* topology_id) {
	g_assert(topology_id);
	// Get the context Id (UUID) from the topologyIdObj
	cJSON* contextIdObj = cJSON_GetObjectItem(obj, "contextId");
	if (cJSON_IsString(contextIdObj)) {
		duplicate_string(topology_id->contextId, contextIdObj->valuestring);
	}
	// Get the topologyId (UUID) from the topologyIdObj
	cJSON* topologyUuidObj = cJSON_GetObjectItem(obj, "topology_uuid");
	if (cJSON_IsString(topologyUuidObj)) {
		duplicate_string(topology_id->topology_uuid, topologyUuidObj->valuestring);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief parsing EndpointIds JSON object
 *
 * 	@param item
 *  @param serviceEndPointId
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_endPointsIds(cJSON* item, struct service_endpoints_id_t* serviceEndPointId) {
	// Get the topology Id Object
	cJSON* topologyIdObj = cJSON_GetObjectItem(item, "topology_id");
	if (cJSON_IsObject(topologyIdObj)) {
		parse_topology_Id(topologyIdObj, &serviceEndPointId->topology_id);
	}
	// Get the deviceId (UUID)
	cJSON* deviceIdObj = cJSON_GetObjectItem(item, "device_id");
	if (cJSON_IsString(deviceIdObj)) {
		duplicate_string(serviceEndPointId->device_uuid, deviceIdObj->valuestring);
		DEBUG_PC("DeviceId: %s", serviceEndPointId->device_uuid);
	}
	// Get the endpointId (UUID)
	cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_uuid");
	if (cJSON_IsString(endPointIdObj)) {
		duplicate_string(serviceEndPointId->endpoint_uuid, endPointIdObj->valuestring);
		DEBUG_PC("EndPointId: %s", serviceEndPointId->endpoint_uuid);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array of Endpoint Ids	of the active services
 *
 * 	@param endPointArray
 *  @param actServ
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_act_service_endPointsIds_array(cJSON* endPointIdArray, struct activeService_t* actServ) {
	g_assert(actServ);

	for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) {
		actServ->num_service_endpoints_id++;
		struct service_endpoints_id_t* serviceEndPointId = &(actServ->service_endpoints_id[i]);
		cJSON* item = cJSON_GetArrayItem(endPointIdArray, i);
		parse_endPointsIds(item, serviceEndPointId);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array of Endpoint Ids	of the requested services
 *
 * 	@param endPointArray
 *  @param s
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_service_endPointsIds_array(cJSON* endPointIdArray, struct service_t* s) {

	for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) {
		s->num_service_endpoints_id++;
		struct service_endpoints_id_t* serviceEndPointId = &(s->service_endpoints_id[i]);

		cJSON* item = cJSON_GetArrayItem(endPointIdArray, i);
		parse_endPointsIds(item, serviceEndPointId);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array with the required service constraints
 *
 * 	@param constraintArray
 *  @param s
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_service_constraints(cJSON* constraintArray, struct service_t* s) {
	for (gint i = 0; i < cJSON_GetArraySize(constraintArray); i++) {
		s->num_service_constraints++;
		struct constraint_t* constraint = &(s->constraints[i]);

		cJSON* item = cJSON_GetArrayItem(constraintArray, i);

		// Get the constraint type
		cJSON* typeObj = cJSON_GetObjectItem(item, "constraint_type");
		if (cJSON_IsString(typeObj)) {
			duplicate_string(constraint->constraint_type, typeObj->valuestring);
		}
		// Get the constraint value
		cJSON* valueObj = cJSON_GetObjectItem(item, "constraint_value");
		if (cJSON_IsString(valueObj)) {
			 duplicate_string(constraint->constraint_value, valueObj->valuestring);
		} 
		DEBUG_PC("Service Reqs [%d] -- Type: %s | Value: %s", i+1, constraint->constraint_type, constraint->constraint_value);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the serviceId information from a JSON obj
 *
 * 	@param obj
 *  @param serviceId
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_json_serviceId(cJSON* obj, struct serviceId_t* serviceId) {
	g_assert(obj);
	g_assert(serviceId);

	// Get context Id uuid
	cJSON* contextIdObj = cJSON_GetObjectItem(obj, "contextId");
	if (cJSON_IsString(contextIdObj)) {
		// convert the string in contextId->valuestring in uuid binary format
		duplicate_string(serviceId->contextId, contextIdObj->valuestring);
		DEBUG_PC("ContextId: %s (uuid string format)", serviceId->contextId);
	}
	// Get service Id uuid
	cJSON* serviceUuidObj = cJSON_GetObjectItem(obj, "service_uuid");
	if (cJSON_IsString(serviceUuidObj)) {
		duplicate_string(serviceId->service_uuid, serviceUuidObj->valuestring);
		DEBUG_PC("Service UUID: %s (uuid string format)", serviceId->service_uuid);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the array with the different
 * network services
 *
 * 	@param serviceArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_serviceList_array(cJSON* serviceArray) {

	for (gint i = 0; i < cJSON_GetArraySize(serviceArray); i++)
	{
		struct service_t* service = g_malloc0(sizeof(struct service_t));
		if (service == NULL) {
			DEBUG_PC("Memory allocation error ...");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(serviceArray, i);
		// Get the algorithm Id
		cJSON* algIdItem = cJSON_GetObjectItem(item, "algId");
		if (cJSON_IsString(algIdItem)) {
			duplicate_string(service->algId, algIdItem->valuestring);
			DEBUG_PC ("algId: %s", service->algId);
			// assumed that all the services request the same algId
			duplicate_string(algId, service->algId);
		}

		// Get the syncPaths
		cJSON* synchPathObj = cJSON_GetObjectItemCaseSensitive(item, "syncPaths");
		if (cJSON_IsBool(synchPathObj)) {
			// Check Synchronization of multiple Paths to attain e.g. global concurrent optimization
			if (cJSON_IsTrue(synchPathObj)) {
				syncPath = TRUE;
				DEBUG_PC("Path Synchronization is required");
			}
			if (cJSON_IsFalse(synchPathObj)) {
				syncPath = FALSE;
				DEBUG_PC("No Path Synchronization");
			}
		}

		// Get service Id in terms of contextId and service uuids
		cJSON* serviceIdObj = cJSON_GetObjectItem(item, "serviceId");
		if (cJSON_IsObject(serviceIdObj)) {
			parse_json_serviceId(serviceIdObj, &service->serviceId);
		}		

		// Get de service type
		cJSON* serviceTypeObj = cJSON_GetObjectItem(item, "serviceType");
		if (cJSON_IsNumber(serviceTypeObj))
		{
			service->service_type = (guint)(serviceTypeObj->valuedouble);
			print_service_type(service->service_type);
		}

		// Get the endPoints array of the service
		cJSON* endPointIdsArray = cJSON_GetObjectItem(item, "service_endpoints_ids");
		if (cJSON_IsArray(endPointIdsArray)) {
			parse_service_endPointsIds_array(endPointIdsArray, service);
		}	

		// Get the service constraints
		cJSON* constraintArray = cJSON_GetObjectItem(item, "service_constraints");
		if (cJSON_IsArray(constraintArray)) {
			parse_service_constraints(constraintArray, service);
		}		

		// Get the maximum number of paths to be computed (kPaths) inspected/explored
		cJSON* kPathsInspObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths_inspection");
		if (cJSON_IsNumber(kPathsInspObj)){
			service->kPaths_inspected = (guint)(kPathsInspObj->valuedouble);
		}

		// Get the maximum number of paths to be computed (kPaths) returned
		cJSON* kPathsRetpObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths_return");
		if (cJSON_IsNumber(kPathsRetpObj)){
			service->kPaths_returned = (guint)(kPathsRetpObj->valuedouble);
		}	
		
		// Append the requested service to the serviceList
		serviceList = g_list_append(serviceList, service);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function parsing the capacity attributes in the endpoint
 *
 * 	@param capacity
 *  @param c
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_capacity_object(cJSON* capacity, struct capacity_t* c) {
	cJSON* totalSizeObj = cJSON_GetObjectItem(capacity, "total-size");
	if (cJSON_IsObject(totalSizeObj)) {
		//Get the capacity value
		cJSON* valueObj = cJSON_GetObjectItem(totalSizeObj, "value");
		if (cJSON_IsNumber(valueObj)) {
			memcpy(&c->value, &valueObj->valuedouble, sizeof (gdouble));
		}
		// Get the Units
		cJSON* unitObj = cJSON_GetObjectItem(totalSizeObj, "unit");
		if (cJSON_IsNumber(unitObj)) {
			c->unit = (guint)(unitObj->valuedouble);
		}	
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function parsing the device endpoints
 *
 * 	@param endPointsArray
 *  @param d
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_json_device_endpoints_array(cJSON* endPointsArray, struct device_t* d) {
	for (gint i = 0; i < cJSON_GetArraySize(endPointsArray); i++) {
		d->numEndPoints++;
		if (d->numEndPoints >= MAX_DEV_ENDPOINT_LENGTH) {
			DEBUG_PC("d->numEndPoints(%d) exceeded MAX_DEV_ENDPOINT_LENGTH(%d)", d->numEndPoints, MAX_DEV_ENDPOINT_LENGTH);
		}
		struct endPoint_t* endpoint = &(d->endPoints[i]);

		cJSON* item = cJSON_GetArrayItem(endPointsArray, i);

		// Get the Endpoint Identifier: topology, context, device and endpointId
		cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_id");
		if (cJSON_IsObject(endPointIdObj)) {
			// Get the topology Id Object
			cJSON* topologyIdObj = cJSON_GetObjectItem(endPointIdObj, "topology_id");
			if (cJSON_IsObject(topologyIdObj)) {
				parse_topology_Id(topologyIdObj, &endpoint->endPointId.topology_id);
			}
			// Get the deviceId
			cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id");
			if (cJSON_IsString(deviceIdObj)) {				
				duplicate_string(endpoint->endPointId.device_id, deviceIdObj->valuestring);
			}
			// Get the endpoint_uuid
			cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid");
			if (cJSON_IsString(endPointUuidObj)) {				
				duplicate_string(endpoint->endPointId.endpoint_uuid, endPointUuidObj->valuestring);
			}
		}
		// Get the EndPoint Type
		cJSON* endPointTypeObj = cJSON_GetObjectItem(item, "endpoint_type");
		if (cJSON_IsString(endPointTypeObj)) {
			duplicate_string(endpoint->endpointType, endPointTypeObj->valuestring);
			//DEBUG_PC("Device Endpoint (%d) -- EndPoint Type: %s", i + 1, endpoint->endpointType);
		}
		// Link Port Direction
		cJSON* linkPortDirectionObj = cJSON_GetObjectItem(item, "link_port_direction");
		if (cJSON_IsNumber(linkPortDirectionObj)) {
			endpoint->link_port_direction = (guint)(linkPortDirectionObj->valuedouble);
			print_link_port_direction(endpoint->link_port_direction);
		}
		// EndPoint Termination Direction
		cJSON* terminationDirectionObj = cJSON_GetObjectItem(item, "termination-direction");
		if (cJSON_IsNumber(terminationDirectionObj)) {
			endpoint->termination_direction = (guint)(terminationDirectionObj->valuedouble);
			print_termination_direction(endpoint->termination_direction);
		}
		// Endpoint Termination State
		cJSON* terminationStateObj = cJSON_GetObjectItem(item, "termination-state");
		if (cJSON_IsNumber(terminationStateObj)) {
			endpoint->termination_state = (guint)(terminationStateObj->valuedouble);
			print_termination_state(endpoint->termination_state);
		}
		// total potential capacity
		cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity");
		if (cJSON_IsObject(totalPotentialCapacityObj))
		{
			parse_capacity_object(totalPotentialCapacityObj, &endpoint->potential_capacity);
			//DEBUG_PC("Device Endpoint (%d) -- Potential Capacity: %f", i + 1, endpoint->potential_capacity.value);
			print_capacity_unit(endpoint->potential_capacity.unit);
		}
		// total available capacity
		cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity");
		if (cJSON_IsObject(availableCapacityObj))
		{
			parse_capacity_object(availableCapacityObj, &endpoint->available_capacity);
			//DEBUG_PC("Device Endpoint (%d) -- Available Capacity: %f", i + 1, endpoint->available_capacity.value);
			print_capacity_unit(endpoint->available_capacity.unit);
		}
		// inter-domain plug-in
		cJSON* interDomainPlugInObj = cJSON_GetObjectItem(item, "inter-domain-plug-in");
		if (cJSON_IsObject(interDomainPlugInObj)) {
			// Get the local
			cJSON* idInterDomainLocal = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-local-id");
			if (cJSON_IsString(idInterDomainLocal)) {
				duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id, idInterDomainLocal->valuestring);
				//DEBUG_PC("Inter-Domain Local Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id);				
			}
			// Get the remote
			cJSON* idInterDomainRemote = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-remote-id");
			if (cJSON_IsString(idInterDomainRemote)) {
				duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id, idInterDomainRemote->valuestring);
				//DEBUG_PC("Inter-Domain Remote Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id);
			}
		}

		// Energy consumption per endPoint port
		cJSON* energyPortObj = cJSON_GetObjectItem(item, "energy_consumption");
		if (cJSON_IsNumber(energyPortObj)) {
			memcpy(&endpoint->energyConsumption, &energyPortObj->valuedouble, sizeof(gdouble));
			DEBUG_PC("Endpoint Energy Consumption: %f", endpoint->energyConsumption);
		}

		// Endpoint Operational Status
		cJSON* operationalStatusObj = cJSON_GetObjectItem(item, "operational_status");
		if (cJSON_IsNumber(operationalStatusObj)) {
			endpoint->operational_status = (gint)(operationalStatusObj->valuedouble);
			DEBUG_PC("Endpoint Operational Status: %d", endpoint->operational_status);
		}
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the set/list of devices forming the context/topology
 *
 * 	@param deviceArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_deviceList_array(cJSON* deviceArray) {
	DEBUG_PC("");
	DEBUG_PC("========= PARSING DEVICE LIST ============");
	for (gint i = 0; i < cJSON_GetArraySize(deviceArray); i++) {		
		struct device_t* d = g_malloc0(sizeof(struct device_t));
		if (d == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(deviceArray, i);

		// Get the power idle of the switch
		cJSON* powerIdleObj = cJSON_GetObjectItem(item, "power_idle");
		if (cJSON_IsNumber(powerIdleObj)) {
			memcpy(&d->power_idle, &powerIdleObj->valuedouble, sizeof(gdouble));
			DEBUG_PC("Power Idle: %f", d->power_idle);
		}

		// Get the operational state
		cJSON* opeStatusObj = cJSON_GetObjectItem(item, "operational_status");
		if (cJSON_IsNumber(opeStatusObj)) {
			d->operational_status = (gint)(opeStatusObj->valuedouble);
			DEBUG_PC("Operational Status: %d (0 Undefined, 1 Disabled, 2 Enabled", d->operational_status);
		}

		// Get the device UUID
		cJSON* deviceUuidObj = cJSON_GetObjectItem(item, "device_Id");
		if (cJSON_IsString(deviceUuidObj)) {
			duplicate_string(d->deviceId, deviceUuidObj->valuestring);
			DEBUG_PC("Device (%d) -- Id: %s (uuid string format)", i + 1, d->deviceId);
		}

		// Get the device Type
		cJSON* deviceTypeObj = cJSON_GetObjectItem(item, "device_type");
		if (cJSON_IsString(deviceTypeObj)) {
			duplicate_string(d->deviceType, deviceTypeObj->valuestring);
			//DEBUG_PC("  Device Type: %s ---", d->deviceType);
		}
		DEBUG_PC("DeviceId: %s, Device Type: %s", d->deviceId, d->deviceType);

		// get the device endPoints
		cJSON* deviceEndpointsArray = cJSON_GetObjectItem(item, "device_endpoints");
		if (cJSON_IsArray(deviceEndpointsArray)) {
			parse_json_device_endpoints_array(deviceEndpointsArray, d);
		}
		// append the device into the deviceList
		deviceList = g_list_append(deviceList, d);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON objects the endPoint of a link
 *
 * 	@param endPointsLinkObj
 *  @param l
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_json_link_endpoints_array(cJSON *endPointsLinkObj, struct link_t* l) {
	for (gint i = 0; i < cJSON_GetArraySize(endPointsLinkObj); i++) {
		//DEBUG_PC("link: %s has %d endPointIds", l->linkId, l->numLinkEndPointIds);
		l->numLinkEndPointIds++;
		struct link_endpointId_t* endPointLink = &(l->linkEndPointId[i]);

		cJSON* item = cJSON_GetArrayItem(endPointsLinkObj, i);

		// Get endPoint attributes (topologyId, deviceId, endpoint_uuid)
		cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_id");
		if (cJSON_IsObject(endPointIdObj)) {
			// Get the topology Id Object
			cJSON* topologyIdObj = cJSON_GetObjectItem(endPointIdObj, "topology_id");
			if (cJSON_IsObject(topologyIdObj)) {
				parse_topology_Id(topologyIdObj, &endPointLink->topology_id);				
			}
			// Get the deviceId
			cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id");
			if (cJSON_IsString(deviceIdObj)) {
				duplicate_string(endPointLink->deviceId, deviceIdObj->valuestring);
				DEBUG_PC("   Link Endpoint[%d] -- DeviceId: %s", i + 1, endPointLink->deviceId);
			}
			// Get the endpoint_uuid
			cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid");
			if (cJSON_IsString(endPointUuidObj)) {
				duplicate_string(endPointLink->endPointId, endPointUuidObj->valuestring);
				//DEBUG_PC("Link Endpoint (%d) -- EndPoint Uuid: %s (uuid)", i + 1, endPointLink->endPointId);
			}
		}
	}
	//DEBUG_PC("link id: %s has %d endpoints", l->linkId, l->numLinkEndPointIds);
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON objects describing the set of links
 *
 * 	@param linkListArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_linkList_array(cJSON* linkListArray) {
	DEBUG_PC("");
	DEBUG_PC("======= PARSING OF THE LINK LIST ARRAY ==========");
	for (gint i = 0; i < cJSON_GetArraySize(linkListArray); i++) {		
		struct link_t* l = g_malloc0(sizeof(struct link_t));
		if (l == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(linkListArray, i);

		// Get the link Id (uuid)
		cJSON* linkIdObj = cJSON_GetObjectItem(item, "link_Id");
		if (cJSON_IsString(linkIdObj)) {
			duplicate_string(l->linkId, linkIdObj->valuestring);
			DEBUG_PC(" * Link (%d) -- Id: %s (uuid)", i + 1, l->linkId);
		}

		// Get the link endpoints (assumed to be p2p)
		cJSON* endPointsLinkObj = cJSON_GetObjectItem(item, "link_endpoint_ids");
		if (cJSON_IsArray(endPointsLinkObj)) {
			//DEBUG_PC("number linkEndPointIds: %d", l->numLinkEndPointIds);
			parse_json_link_endpoints_array(endPointsLinkObj, l);
		}
		// get the fowarding direction
		cJSON* fwdDirObj = cJSON_GetObjectItem(item, "forwarding_direction");
		if (cJSON_IsNumber(fwdDirObj)) {
			l->forwarding_direction = (guint)(fwdDirObj->valuedouble);
			print_link_forwarding_direction(l->forwarding_direction);
		}
		// total potential capacity
		cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity");
		if (cJSON_IsObject(totalPotentialCapacityObj))
		{
			parse_capacity_object(totalPotentialCapacityObj, &l->potential_capacity);
			//DEBUG_PC("Link (%d) -- Potential Capacity: %f", i + 1, l->potential_capacity.value);
			print_capacity_unit(l->potential_capacity.unit);
		}
		// total available capacity
		cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity");
		if (cJSON_IsObject(availableCapacityObj))
		{
			parse_capacity_object(availableCapacityObj, &l->available_capacity);
			//DEBUG_PC("Link (%d) -- Available Capacity: %f", i + 1, l->available_capacity.value);
			print_capacity_unit(l->available_capacity.unit);
		}
		// Cost Characteristics
		cJSON* costCharacObj = cJSON_GetObjectItem(item, "cost-characteristics");
		if (cJSON_IsObject(costCharacObj)) {
			// Cost Name
			cJSON* costNameObj = cJSON_GetObjectItem(costCharacObj, "cost-name");
			if (cJSON_IsString(costNameObj)) {
				duplicate_string(l->cost_characteristics.cost_name, costNameObj->valuestring);
				//DEBUG_PC("Link (%d) -- Cost Name: %s", i + 1, l->cost_characteristics.cost_name);
			}
			// Cost value
			cJSON* costValueObj = cJSON_GetObjectItem(costCharacObj, "cost-value");
			if (cJSON_IsString(costValueObj)) {
				char* endpr;
				l->cost_characteristics.cost_value = (gdouble)(strtod(costValueObj->valuestring, &endpr));
				//DEBUG_PC("Link (%d) -- Cost Value: %f", i + 1, l->cost_characteristics.cost_value);
			}
			// Cost Algorithm
			cJSON* costAlgObj = cJSON_GetObjectItem(costCharacObj, "cost-algorithm");
			if (cJSON_IsString(costAlgObj)) {
				char* endpr;
				l->cost_characteristics.cost_algorithm = (gdouble)(strtod(costAlgObj->valuestring, &endpr));
				//DEBUG_PC("Link (%d) -- Cost Algorithm: %f", i + 1, l->cost_characteristics.cost_algorithm);
			}
		}
		// Latency Characteristics
		cJSON* latencyCharacObj = cJSON_GetObjectItem(item, "latency-characteristics");
		if (cJSON_IsObject(latencyCharacObj)) {
			cJSON* fixedLatencyCharacObj = cJSON_GetObjectItem(latencyCharacObj, "fixed-latency-characteristic");
			if (cJSON_IsString(fixedLatencyCharacObj)) {
				char* endpr;
				l->latency_characteristics.fixed_latency = (gdouble)(strtod(fixedLatencyCharacObj->valuestring, &endpr));
				//DEBUG_PC("Link (%d) -- Latency: %f", i + 1, l->latency_characteristics.fixed_latency);
			}	
		}
		linkList = g_list_append(linkList, l);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to generate the reverse (unidirecitonal) link from those being learnt
 *  from the received context
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 ////////////////////////////////////////////////////////////////////////////////////////
void generate_reverse_linkList() {
	DEBUG_PC("");
	DEBUG_PC("CREATION OF REVERSE LINKS");
	gint numLinks = g_list_length (linkList);
	DEBUG_PC("Initial Number of links in the main List: %d", numLinks);
	gint i = 0;
	for (GList* ln = g_list_first(linkList);
		(ln) && (i < numLinks);
		ln = g_list_next(ln), i++)
	{
		struct link_t* refLink = (struct link_t*)(ln->data);
		struct link_t* newLink = g_malloc0(sizeof(struct link_t));
		if (newLink == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		// Copy the linkId + appending "_rev"
		duplicate_string(newLink->linkId, refLink->linkId);
		strcat(newLink->linkId, "_rev"); 		

		//DEBUG_PC("refLink: %s // newLink: %s", refLink->linkId, newLink->linkId);

		// Assumption: p2p links. The newLink endpoints are the reversed ones form the reference Link (refLink)
		// i.e., refLink A->B, then newLink B->A
		//DEBUG_PC("ref: %s has %d endpoints", refLink->linkId, refLink->numLinkEndPointIds);
#if 1
		if (refLink->numLinkEndPointIds != 2) {
			DEBUG_PC("To construct the new Link from ref: %s, 2 EndPoints are a MUST", refLink->linkId);
			exit(-1);
		}
#endif
		//DEBUG_PC(" * Link[%d] -- Id: %s", numLinks + i, newLink->linkId);

		//DEBUG_PC("Number of Endpoints in Link: %d", refLink->numLinkEndPointIds);
		for (gint j = refLink->numLinkEndPointIds - 1, m = 0; j >= 0; j--, m++) {			
			struct link_endpointId_t* refEndPId = &(refLink->linkEndPointId[j]);
			struct link_endpointId_t* newEndPId = &(newLink->linkEndPointId[m]);
			// Duplicate the topologyId information, i.e., contextId and topology_uuid
			duplicate_string(newEndPId->topology_id.contextId, refEndPId->topology_id.contextId);
			duplicate_string(newEndPId->topology_id.topology_uuid, refEndPId->topology_id.topology_uuid);
			//duplicate the deviceId and endPoint_uuid
			duplicate_string(newEndPId->deviceId, refEndPId->endPointId);
			duplicate_string(newEndPId->endPointId, refEndPId->deviceId);
			//DEBUG_PC("refLink Endpoint[%d]: %s(%s)", j, refEndPId->deviceId, refEndPId->endPointId);
			//DEBUG_PC("newLink Endpoint[%d]: %s(%s)", m, newEndPId->deviceId, newEndPId->endPointId);
			newLink->numLinkEndPointIds++;
		}

		// duplicate forwarding direction
		newLink->forwarding_direction = refLink->forwarding_direction;

		// duplicate capacity attributes
		memcpy(&newLink->potential_capacity.value, &refLink->potential_capacity.value, sizeof(gdouble));
		newLink->potential_capacity.unit = refLink->potential_capacity.unit;

		memcpy(&newLink->available_capacity.value, &refLink->available_capacity.value, sizeof(gdouble));
		newLink->available_capacity.unit = refLink->available_capacity.unit;

		// duplicate cost characteristics
		memcpy(&newLink->cost_characteristics.cost_value, &refLink->cost_characteristics.cost_value, sizeof(gdouble));
		memcpy(&newLink->cost_characteristics.cost_algorithm, &refLink->cost_characteristics.cost_algorithm, sizeof(gdouble));
		duplicate_string(newLink->cost_characteristics.cost_name, refLink->cost_characteristics.cost_name);

		// duplicate latency characteristics
		memcpy(&newLink->latency_characteristics.fixed_latency, &refLink->latency_characteristics.fixed_latency, sizeof(gdouble));
		// Append in the linkList the new creted Link
		linkList = g_list_append(linkList, newLink);
	}
	DEBUG_PC("Terminating Reverse Links [total links: %d]", g_list_length(linkList));
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON object/s for active services
 *
 * 	@param actServiceArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_activeService_array(cJSON* actServiceArray) {
	DEBUG_PC("");
	DEBUG_PC("====== PARSING THE JSON CONTENTS OF THE ACTIVE SERVICES =======");
	
	for (gint i = 0; i < cJSON_GetArraySize(actServiceArray); i++) {
		struct activeService_t* actServ = g_malloc0(sizeof(struct activeService_t));
		if (actServ == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(actServiceArray, i);
		// ServiceId
		cJSON* serviceIdObj = cJSON_GetObjectItem(item, "serviceId");
		if (cJSON_IsObject(serviceIdObj)) {
			parse_json_serviceId(serviceIdObj, &actServ->serviceId);
		}
		// Service Type
		cJSON* serviceTypeObj = cJSON_GetObjectItem(item, "serviceType");
		if (cJSON_IsNumber(serviceTypeObj))
		{
			actServ->service_type = (guint)(serviceTypeObj->valuedouble);
			print_service_type(actServ->service_type);
		}
		// Service Endpoints
		cJSON* endPointIdsArray = cJSON_GetObjectItem(item, "service_endpoints_ids");
		if (cJSON_IsArray(endPointIdsArray)) {
			parse_act_service_endPointsIds_array(endPointIdsArray, actServ);
		}
		// Parsing the active service path
		actServ->activeServPath = NULL;
		cJSON* actServPathArray = cJSON_GetObjectItem(item, "devices");
		if (cJSON_IsArray(endPointIdsArray)) {
			for (gint j = 0; j < cJSON_GetArraySize(actServPathArray); j++) {
				struct activeServPath_t* actServPath = g_malloc0(sizeof(struct activeServPath_t));
				if (actServPath == NULL) {
					DEBUG_PC("Memory Allocation Failure");
					exit(-1);
				}
				cJSON* item2 = cJSON_GetArrayItem(item, j);
				// Topology Id
				cJSON* topologyIdObj = cJSON_GetObjectItem(item2, "topology_id");
				if (cJSON_IsObject(topologyIdObj)) {
					parse_topology_Id(topologyIdObj, &actServPath->topology_id);
				}
				// Device Id
				cJSON* deviceIdObj = cJSON_GetObjectItem(item2, "device_id");
				if (cJSON_IsString(deviceIdObj)) {
					duplicate_string(actServPath->deviceId, deviceIdObj->valuestring);
				}
				// EndPointId
				cJSON* endPointUUIDObj = cJSON_GetObjectItem(item2, "endpoint_uuid");
				if (cJSON_IsString(endPointUUIDObj)) {
					duplicate_string(actServPath->endPointId, endPointUUIDObj->valuestring);
				}
				// Append element from the Active Service Path (i.e.,topologyId, deviceId and endpointId)
				actServ->activeServPath = g_list_append(actServ->activeServPath, actServPath);
			}
		}
		// append into the Actice Service List
		activeServList = g_list_append(activeServList, actServ);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON object/s for the PATH COMP request (i.e. service
 *  requests, device and links)
 * 	
 * 	@param root
 * 	@param source
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_obj_pathComp_request(cJSON * root, GIOChannel * source)
{
	// Set of services to seek their path and resource selection
	cJSON* serviceListArray = cJSON_GetObjectItem(root, "serviceList");
	if (cJSON_IsArray(serviceListArray)) {
		parsing_json_serviceList_array(serviceListArray);
	}   
    
	// Get the deviceList
	cJSON* deviceListArray = cJSON_GetObjectItem(root, "deviceList");
	if (cJSON_IsArray(deviceListArray)) {
		parsing_json_deviceList_array(deviceListArray);
	}

	// Get the linkList
	cJSON* linkListArray = cJSON_GetObjectItem(root, "linkList");
	if (cJSON_IsArray(linkListArray)) {
		parsing_json_linkList_array(linkListArray);

		// In the context information, if solely the list of links are passed for a single direction, 
		// the reverse direction MUST be created sythetically 
		// LGR: deactivated; link duplication needs to be done smartly with TAPI. done manually in topology by now
		//generate_reverse_linkList();
	}

	// Get the list of active services
	cJSON* actServiceArray = cJSON_GetObjectItem(root, "activeServList");
	if (cJSON_IsArray(actServiceArray)) {
		parsing_json_activeService_array(actServiceArray);
	}
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used parse the JSON object/s 
 * 	
 * 	@param data
 *  @param source
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint parsing_json_obj (guchar *data, GIOChannel *source) {
    cJSON * root = cJSON_Parse((const char *)data);
    char * print = cJSON_Print(root);  

	DEBUG_PC("STARTING PARSING JSON CONTENTS");
	parsing_json_obj_pathComp_request (root, source);
	DEBUG_PC("ENDING PARSING JSON CONTENTS");
	// Release the root JSON object variable
	cJSON_free (root);
	g_free(print);
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Create new tcp client connected to PATH COMP
 * 
 * 	@param channel_client, GIOChannel
 *  @param fd
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
struct pathComp_client * RESTapi_client_create (GIOChannel * channel_client, gint fd) {
	/** check values */
	g_assert(channel_client != NULL); 

	struct pathComp_client* client = g_malloc0 (sizeof (struct pathComp_client));
	if (client == NULL )
	{
		DEBUG_PC ("Malloc for the client failed");
		exit(-1);
	}  

	/** Make client input/output buffer. */
	client->channel = channel_client;	
	client->obuf = stream_new(MAXLENGTH);
	client->ibuf = stream_new(MAXLENGTH);
	client->fd = fd;

	// Clients connected to the PATH COMP SERVER
	CLIENT_ID++;
	client->type = CLIENT_ID;

	//DEBUG_PC ("Client Id: %u is created (%p)", client->type, client);
	//DEBUG_PC ("Client ibuf: %p || obuf: %p", client->ibuf, client->obuf);

	// Add the tcp client to the list
	RESTapi_tcp_client_list = g_list_append (RESTapi_tcp_client_list, client);
	//DEBUG_PC ("Num of TCP Clients: %d", g_list_length (RESTapi_tcp_client_list));
	return client;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Close the tcp client, removing from the rapi_tcp_client_list
 * 
 * 	@param client
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_client_close (struct pathComp_client* client) {
	//DEBUG_PC("Closing the client (Id: %d) %p", client->type, client);
	//DEBUG_PC("Client ibuf: %p || obuf: %p", client->ibuf, client->obuf);
	
	if (client->ibuf != NULL) {
		//DEBUG_PC("Client ibuf: %p", client->ibuf);
		stream_free(client->ibuf);
		client->ibuf = NULL;
	}
	if (client->obuf != NULL) {
		//DEBUG_PC("Client obuf: %p", client->obuf);
		stream_free(client->obuf);
		client->obuf = NULL;
	}
	// Remove from the list
	RESTapi_tcp_client_list = g_list_remove (RESTapi_tcp_client_list, client);
	//DEBUG_PC ("TCP Client List: %d", g_list_length(RESTapi_tcp_client_list));
	 
	g_free (client);
	client = NULL;	 
	DEBUG_PC ("client has been removed ...");	 
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Close operations over the passed tcp channel
 * 
 * 	@param source
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_close_operations (GIOChannel * source)	{
	gint fd = g_io_channel_unix_get_fd (source);
	
	//DEBUG_PC ("Stop all the operations over the fd: %d", fd);	
	g_io_channel_flush(source, NULL);
	GError *error = NULL;    
	g_io_channel_shutdown (source, TRUE, &error);
	if(error) {
		DEBUG_PC ("An error occurred ...");
	}
	g_io_channel_unref (source);
	return;	
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Remove the client and close operations over the TCP connection
 * 
 * 	@param client
 *  @param source
 *  @param fd
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_stop (struct pathComp_client* client, GIOChannel * source, gint fd) {
	
	DEBUG_PC("Client Socket: %d is Stopped", fd);
	// remove client
	RESTapi_client_close(client);
	// Stop operations over that channel
	RESTapi_close_operations(source);
	close (fd);
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the different lines ending up in \r\n
 * 	
 * 	@param s
 * 	@param buf
 * 	@param size
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint RESTapi_get_line (GIOChannel *channel, gchar *buf, gint size) {
    gint i = 0;
    //DEBUG_PC ("\n");
    //DEBUG_PC ("----- Read REST API Line(\r\n) ------");
    gint n = 0;
    guchar c = '\0'; // END OF FILE    
    gboolean cr = FALSE;
    while (i < size - 1) {
		n = read_channel (channel, &c, 1);		
		if (n == -1) {
			//DEBUG_PC ("Close the channel and eliminate the client");
			return -1;			
		}	
		if (n > 0) {
			//DEBUG_PC ("%c", c);
			buf[i] = c;
			i++;	
			if (c == '\r') 			{
				cr = TRUE;	      
			}	  
			if ((c == '\n') && (cr == TRUE)) 			{	   
				break;
			}	        
		} 
		else {
			c = '\n';
			buf[i] = c;
			i++;
			break;
		}
    }
    buf[i] = '\0';    
    //DEBUG_PC ("Line (size: %d) buf: %s", i, buf);
    return i;
}  

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the HTTP method
 * 	
 * 	@param buf
 * 	@param j
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
guint RESTapi_get_method (gchar *buf, gint *j)
{
	guint RestApiMethod = 0;
	gchar method[255];
	gint i = 0;	
	while (!ISspace(buf[*j]) && (i < sizeof(method) - 1)) {
		method[i] = buf[*j];
		i++; 
		*j = *j + 1;
	}
	method[i] = '\0';
	DEBUG_PC ("REST API METHOD: %s", method);	
	
	// Check that the methods are GET, POST or PUT
	if (strcasecmp((const char *)method, "GET") && strcasecmp((const char *)method, "POST") && 
		strcasecmp ((const char *)method, "HTTP/1.1") && strcasecmp ((const char *)method, "PUT")) {
		DEBUG_PC ("%s is not a method ...", method);
		return RestApiMethod;	
	}
	// Method selector
	if (strncmp ((const char*)method, "GET", 3) == 0) {
		RestApiMethod = REST_API_METHOD_GET;		
	}
	else if (strncmp ((const char*)method, "POST", 4) == 0) {
		RestApiMethod = REST_API_METHOD_POST;
	}	
	else if (strncmp ((const char *)method, "HTTP/1.1", 8) == 0) {
		RestApiMethod = REST_API_METHOD_HTTP;
	}
	else if (strncmp ((const char *)method, "PUT", 3) == 0) {
		RestApiMethod = REST_API_METHOD_PUT;
	}	
	return RestApiMethod;	
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to check whether it is a supported method, and return the associated numerical id
 *
 * 	@param method
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
guint is_rest_api_method(char *method) {
	guint RestApiMethod = 0;
	if (strcasecmp((const char*)method, "GET") && strcasecmp((const char*)method, "POST") &&
		strcasecmp((const char*)method, "HTTP/1.1") && strcasecmp((const char*)method, "PUT")) {
		DEBUG_PC("The method: %s is not currently supported ...", method);
		return RestApiMethod;
	}
	// Method selector
	if (strncmp((const char*)method, "GET", 3) == 0) {
		RestApiMethod = REST_API_METHOD_GET;
	}
	else if (strncmp((const char*)method, "POST", 4) == 0) {
		RestApiMethod = REST_API_METHOD_POST;
	}
	else if (strncmp((const char*)method, "HTTP/1.1", 8) == 0) {
		RestApiMethod = REST_API_METHOD_HTTP;
	}
	else if (strncmp((const char*)method, "PUT", 3) == 0) {
		RestApiMethod = REST_API_METHOD_PUT;
	}
	return RestApiMethod;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the url
 * 	
 * 	@param buf
 * 	@param j
 *  @param url
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint get_url (gchar *buf, gint *j, gchar *url)
{
	// Skip space char
	while (ISspace(buf[*j]) && (*j < strlen(buf))) {
		*j = *j + 1;
	}
	
	//DEBUG_PC ("buf[%d]: %c", *j, buf[*j]);
	int result = isspace (buf[*j]);	
	*buf = *buf + *j;
	gint numChar = 0;
	gint initChar = *j;
	result = 0;
	while (result == 0)	{
		*j = *j + 1;
		result = isspace (buf[*j]);
		numChar++;
	}
	//DEBUG_PC ("numChar: %d", numChar);
	memcpy (url, buf + initChar, numChar);
	url[numChar] = '\0';
	//DEBUG_PC ("url: %s", url);
	return numChar;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the version
 * 	
 * 	@param buf
 * 	@param j
 *  @param version
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint get_version (gchar *buf, gint *j, gchar *version) {
	// Skip space char
	while (ISspace(buf[*j]) && (*j < strlen(buf))) {
		*j = *j + 1;
	}	
	//DEBUG_PC ("buf[%d]: %c", *j, buf[*j]);
	int result = isspace (buf[*j]);	
	*buf = *buf + *j;
	gint numChar = 0;
	gint initChar = *j;
	result = 0;
	while (result == 0)	{
		*j = *j + 1;
		result = isspace (buf[*j]);
		numChar++;
	}
	//DEBUG_PC ("numChar: %d", numChar);
	memcpy (version, buf + initChar, numChar);
	version[numChar] = '\0';
	//DEBUG_PC ("version: %s", version);
	return numChar;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to trigger the route computation for the network connectivity service
 *  List and retrieve the result
 * 	
 * 	@param compRouteList
 * 	@param raId
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint triggering_routeComp (struct compRouteOutputList_t *compRouteList, gchar *algId) {
	g_assert (compRouteList);	
	gint httpCode = HTTP_RETURN_CODE_OK;
	DEBUG_PC("Requested Algorithm: %s", algId);
	//////////////////// Algorithm Selector (RAId)//////////////////////////////////////	
	// KSP algorithm
	if (strncmp ((const char*)algId, "KSP", 3) == 0) {
		DEBUG_PC ("Alg Id: KSP");
		httpCode = pathComp_ksp_alg(compRouteList);
	}
	// simple SP algorithm
	else if (strncmp((const char*)algId, "SP", 2) == 0) {
		DEBUG_PC("Alg Id: SP");
		httpCode = pathComp_sp_alg(compRouteList);
	}
	// energy-aware routing
	else if (strncmp((const char*)algId, "EAR", 3) == 0) {
		DEBUG_PC("Alg Id: Energy Aware Routing, EAR");
		httpCode = pathComp_ear_alg(compRouteList);
	}
	return httpCode;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to process the REST API commands
 * 	
 * 	@param source
 * 	@param cond
 * 	@param data
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gboolean RESTapi_activity(GIOChannel *source, GIOCondition cond, gpointer data) {  
	/** some checks */
	g_assert(source != NULL);
	g_assert(data != NULL);	
	
	gchar buf[1024];
	gchar version[255];
	gchar http_result[255];
	gint body_length = 0;	

	struct pathComp_client *client = (struct pathComp_client*)(data);
	DEBUG_PC (" ************************************************************************** ");    
	DEBUG_PC ("                      REST API ACTIVITY Triggered ");
	DEBUG_PC (" ************************************************************************** ");   

	gint fd = g_io_channel_unix_get_fd (source);
	DEBUG_PC ("fd: %d, cond: %d", fd, cond);

	if (cond != G_IO_IN) {
		DEBUG_PC ("Something happening with the channel and fd ... (cond: %d)", cond);
		RESTapi_stop(client, source, fd);
		return FALSE;
	}	
	// Clear input buffer
	stream_reset (client->ibuf);

	// get line
	gint nbytes = RESTapi_get_line (source, buf, sizeof (buf));
	if (nbytes == -1) {
		DEBUG_PC ("nbytes -1 ... CLOSE CLIENT FD and eliminate CLIENT");						
		RESTapi_stop(client, source, fd);
		return FALSE;						
	}	
	if ((buf[0] == '\n') && (nbytes  == 1))
	{
		//DEBUG_PC (" -- buf[0] = newline --");
		RESTapi_stop(client, source, fd);
		return FALSE;
	}
	
	gint i = 0, j = 0;

	while (1) {
		DEBUG_PC("%s", buf);
		char word[255];		
		while (!ISspace(buf[j]) && (i < sizeof(word) - 1)) {
			word[i] = buf[j]; i++; j++;
		}
		word[i] = '\0';
		// Check if word is bound to a Method, i.e., POST, GET, HTTP/1.1.
		guint method = is_rest_api_method(word);
		if (method == 0) {
			 // ignore other REST fields i.e., Host:, User-Agent:, Accept: ....			
			break;
		}
		// word is bound to a known / supported REST Method
		else {
			gchar url[255];
			i = get_url(buf, &j, url);
			url[i] = '\0';
			// GET - used for checking status of pathComp ... used url /pathComp/api/v1/health
			if (method == REST_API_METHOD_GET) {
				if (strncmp((const char*)url, "/health", 7) != 0) {
					DEBUG_PC("unknown url [%s] for GET method -- Heatlh function", url);
					RESTapi_stop(client, source, fd);
					exit(-1);
				}
				else {
					DEBUG_PC("Sending API Response OK to health requests");
					rapi_response_ok(source, HTTP_RETURN_CODE_OK, NULL);
					return TRUE;
				}
			}
			// for method POST, PUT check that the url is "/pathComp"
			if (method == REST_API_METHOD_POST) {
				if (strncmp((const char*)url, "/pathComp/api/v1/compRoute", 26) != 0) {
					DEBUG_PC("Unknown url: %s", url);
					RESTapi_stop(client, source, fd);
					exit(-1);
				}
			}
			// get the version	
			i = get_version(buf, &j, version);
			version[i] = '\0';
			break;
		}
	}
	// Assume HTTP/1.1, then there is Host Header
	memset(buf, '\0', sizeof(buf));        
	nbytes = RESTapi_get_line(source, buf, sizeof (buf));
	if (nbytes == -1) {
		DEBUG_PC ("nbytes -1 ... then close the fd and eliminate associated client");			
		RESTapi_stop(client, source, fd);
		return FALSE;					
	}	
	
	// Headers --- The Header Fields ends up with a void line (i.e., \r\n)
	while ((nbytes > 0) && (strcmp ("\r\n", (const char *)buf) != 0)) {	
		/* read & discard headers */
		memset(buf, '\0', sizeof(buf));  
		nbytes = RESTapi_get_line (source, buf, sizeof (buf));
		if (nbytes == -1) {
			DEBUG_PC ("nbytes -1 ... then close the fd and eliminate associated client");	
			RESTapi_stop(client, source, fd);
			return FALSE;
		}
		//DEBUG_PC ("Header: %s", buf);	  
		if (strncmp ((const char *)buf, "Content-Length:", 15) == 0) {
			//DEBUG_PC ("Header Content-Length Found");
			gchar str[20];
	  
			gint i = 15, k = 0;  // "Content-Length:" We skip the first 16 characters to directly retrieve the length in bytes of the Body of Request
			gchar contentLength[255];
			memset (contentLength, '\0', sizeof (contentLength));			
			while (buf[i] != '\r') {
				//DEBUG_PC ("%c", buf[i]);
				str[k] = buf[i];
				k++, i++;
			}
			str[k] = '\0';			
			j = 0, i = 0;
			while (ISspace(str[j]) && (j < strlen(str))) {
				j++;
			}
			while (j < strlen(str)) {
				contentLength[i] = str[j];
				i++; j++;
			}
			contentLength[i] = '\0';			
			body_length = atoi (contentLength);
			DEBUG_PC ("Body length: %d (%s) in Bytes", body_length, contentLength);
		}	  
	}
	DEBUG_PC("Read Entire HTTP Header");
	if (body_length == 0) {
		DEBUG_PC ("--- NO REST API Body length (length = %d) ---", body_length);
		return TRUE;
	}       
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Processing Body of the Request
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	DEBUG_PC ("REST API Request - Body -");
	nbytes = read_channel (source, (guchar *)(client->ibuf->data + client->ibuf->putp), body_length);
	if ((nbytes < 0) && (body_length > 0)) 	{
		DEBUG_PC ("nbytes: %d; body_length: %d", nbytes, body_length);
		exit (-1);
	}	
	client->ibuf->putp += nbytes;
	client->ibuf->endp += nbytes;		
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Parsing the contents of the Request
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// build the device list	
	deviceList = NULL;
	// build the link list
	linkList = NULL;
	// Create the network connectivity service list
	serviceList = NULL;
	// Create the active service List
	activeServList = NULL;
	
	DEBUG_PC("Parsing JSON...");
	// Process the json contents and store relevant information at Device, Link,
	// and network connectivity service
	gint ret = parsing_json_obj (client->ibuf->data, source);	
	if (ret == -1) 	{
		DEBUG_PC ("Something wrong with the JSON Objects ... ");		
		RESTapi_stop(client, source, fd);
		return FALSE;
	}
	DEBUG_PC("Parsing done");
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////		
	// Trigger the path computation	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	DEBUG_PC ("Triggering the computation");
	struct compRouteOutputList_t *compRouteOutputList = create_route_list ();
	gint httpCode = triggering_routeComp (compRouteOutputList, algId);	

	// Send the response to the REST  API Client
	if (httpCode != HTTP_RETURN_CODE_OK) {
		DEBUG_PC ("HTTP CODE: %d -- NO OK", httpCode);
		rapi_response (source, httpCode);
	}
	else {
		DEBUG_PC ("HTTP CODE: %d -- OK", httpCode);
		rapi_response_ok (source, httpCode, compRouteOutputList);            
	}
	
	// Release the variables		
	g_free (compRouteOutputList);	
	g_list_free_full(g_steal_pointer(&linkList), (GDestroyNotify)destroy_link);
	g_list_free_full(g_steal_pointer(&deviceList), (GDestroyNotify)destroy_device);
	g_list_free_full(g_steal_pointer(&serviceList), (GDestroyNotify)destroy_requested_service);
	g_list_free_full(g_steal_pointer(&activeServList), (GDestroyNotify)destroy_active_service);
	return TRUE;  
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to accept a new connection and add the client to list of clients
 * 
 * 	@param source, GIOChannel
 * 	@param cond, GIOCondition
 * 	@param data, gpointer
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gboolean RESTapi_tcp_new_connection(GIOChannel *source, GIOCondition cond, gpointer data) {
	DEBUG_PC (" ****** New TCP Connection (REST API) ******");
	/** get size of client_addre structure */
	struct sockaddr_in client_addr;
	socklen_t client = sizeof(client_addr);
	
	if ((cond == G_IO_HUP) || (cond == G_IO_ERR) || (G_IO_NVAL)) {
		//DEBUG_PC ("Something happening with the channel and fd ... cond: %d", cond);		
		// Find the associated client (by the fd) and remove from PATH COMP client list. 
		// Stop all the operations over that PATH COMP client bound channel
		struct pathComp_client *pathComp_client = NULL;
		gint fd = g_io_channel_unix_get_fd (source);
		GList *found = g_list_find_custom (RESTapi_tcp_client_list, &fd, find_rl_client_by_fd);
		if (found != NULL) 	{
			pathComp_client = (struct pathComp_client*)(found->data);
			// remove client
			RESTapi_client_close(pathComp_client);
			// Stop operations over that channel
			RESTapi_close_operations(source);
			close (fd);
			return FALSE;
		}
	}
	if (cond == G_IO_IN) 	{
		gint new = accept(g_io_channel_unix_get_fd(source), (struct sockaddr*)&client_addr, &client);
		if (new < 0) {
			//DEBUG_PC ("Unable to accept new connection");
			return FALSE;
		}

		// new channel
		GIOChannel * new_channel = g_io_channel_unix_new (new);		
		//DEBUG_PC ("TCP Connection (REST API) is UP; (socket: %d)", new);
		// create pathComp client		
		struct pathComp_client *new_client = RESTapi_client_create (new_channel, new);
		
		// force binary encoding with NULL
		GError *error = NULL;
		if ( g_io_channel_set_encoding (new_channel, NULL, &error) != G_IO_STATUS_NORMAL) {		
			DEBUG_PC ("Error: %s", error->message);
			exit (-1);
		}
		g_io_channel_set_close_on_unref (new_channel, TRUE);
		// On unbuffered channels, it is safe to mix read
		// & write calls from the new and old APIs.
		g_io_channel_set_buffered (new_channel, FALSE);
		if (g_io_channel_set_flags (new_channel, G_IO_FLAG_NONBLOCK, &error) != G_IO_STATUS_NORMAL ) {
			DEBUG_PC ("Error: %s", error->message);
			exit (-1);
		}
		//Adds the new channel into the main event loop.
		g_io_add_watch (new_channel, G_IO_IN, RESTapi_activity, new_client);
    }	
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief enabling the reuse of the addr for the Server TCP
 * 	
 * 	@param sock
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_tcp_enable_reuseaddr (gint sock)
{
	gint tmp = 1;
	if (sock < 0)
	{
		DEBUG_PC (" socket: %d !!!",sock);
		exit (-1);
	}
	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (gchar *)&tmp, sizeof (tmp)) == -1)
	{
		DEBUG_PC ("bad setsockopt ...");
		exit (-1);
	}
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Main function for the creating / maintaining TCP session for the REST API
 *
 *  @ port 
 * 
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void RESTapi_init(gint port)
{     
    DEBUG_PC ("REST API PORT (listening): %d", port);     
	
	// File Descriptor - FD - for the socket
	gint s = socket (AF_INET, SOCK_STREAM, 0);
	if (s == -1)
	{
		DEBUG_PC ("Socket creation: FAILED!!");
		exit (-1);
	}
	DEBUG_PC (" CREATED TCP Connection [@fd: %d]", s);

	// Re-bind
	RESTapi_tcp_enable_reuseaddr(s);	
	struct sockaddr_in addr;
	memset (&addr, 0, sizeof (addr));
	addr.sin_family       = AF_INET;
	addr.sin_port         = htons ((u_short)port);
	addr.sin_addr.s_addr  = INADDR_ANY;      

	// Associate IP address and Port to the created socket
	if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
	{
		close (s);
		DEBUG_PC ("Socket bind: FAILED!!");
		exit (-1);
	}
	DEBUG_PC ("Bind to Fd: %d DONE!!", s);

	/** Set up queue for incoming connections */
	if (listen (s, 10) == -1)
	{
		close (s);
		DEBUG_PC ("Socket listen: FAILED!!");
		exit (-1);
	}
	
	//DEBUG_PC ("Listen (up to 10) to Fd: %d Done", s);

	/** Create NEW channel to handle the socket operations*/
	GIOChannel *channel = g_io_channel_unix_new (s);
	gsize buffersize = g_io_channel_get_buffer_size (channel);
	//DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);

	gsize newBufferSize = MAX_GIO_CHANNEL_BUFFER_SIZE;
	g_io_channel_set_buffer_size (channel, newBufferSize);
	buffersize = g_io_channel_get_buffer_size (channel);

	//DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
	//DEBUG_PC ("Channel associated to fd: %d is created", s);
	
	// Adds the new channel into the main event loop.
	g_io_add_watch (channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, RESTapi_tcp_new_connection, NULL);
	return;     
}