/**
 * Copyright (c) 2020 Paul-Louis Ageneau
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "juice.h"
#include "addr.h"
#include "agent.h"
#include "ice.h"

#ifndef NO_SERVER
#include "server.h"
#endif

#include <stdio.h>
#include <string.h>

JUICE_EXPORT juice_agent_t *juice_create(const juice_config_t *config) {
	if (!config)
		return NULL;

	return agent_create(config);
}

JUICE_EXPORT void juice_destroy(juice_agent_t *agent) {
	if (agent)
		agent_destroy(agent);
}

JUICE_EXPORT int juice_gather_candidates(juice_agent_t *agent) {
	if (!agent)
		return JUICE_ERR_INVALID;

	if (agent_gather_candidates(agent) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
}

JUICE_EXPORT int juice_get_local_description(juice_agent_t *agent, char *buffer, size_t size) {
	if (!agent || (!buffer && size))
		return JUICE_ERR_INVALID;

	if (agent_get_local_description(agent, buffer, size) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
}

JUICE_EXPORT int juice_set_remote_description(juice_agent_t *agent, const char *sdp) {
	if (!agent || !sdp)
		return JUICE_ERR_INVALID;

	return agent_set_remote_description(agent, sdp);
}

JUICE_EXPORT int juice_add_remote_candidate(juice_agent_t *agent, const char *sdp) {
	if (!agent || !sdp)
		return JUICE_ERR_INVALID;

	return agent_add_remote_candidate(agent, sdp);
}

JUICE_EXPORT int juice_add_turn_server(juice_agent_t *agent, const juice_turn_server_t *turn_server) {
	if (!agent || !turn_server)
		return JUICE_ERR_INVALID;

	if (agent_add_turn_server(agent, turn_server) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
}

JUICE_EXPORT int juice_set_remote_gathering_done(juice_agent_t *agent) {
	if (!agent)
		return JUICE_ERR_INVALID;

	if (agent_set_remote_gathering_done(agent) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
}

JUICE_EXPORT int juice_send(juice_agent_t *agent, const char *data, size_t size) {
	return juice_send_diffserv(agent, data, size, 0);
}

JUICE_EXPORT int juice_send_diffserv(juice_agent_t *agent, const char *data, size_t size, int ds) {
	if (!agent || (!data && size))
		return JUICE_ERR_INVALID;

	int ret = agent_send(agent, data, size, ds);
	if(ret >= 0)
		return JUICE_ERR_SUCCESS;
	if(ret == -SEAGAIN || ret == -SEWOULDBLOCK)
		return JUICE_ERR_AGAIN;
	if(ret == -SEMSGSIZE)
		return JUICE_ERR_TOO_LARGE;
	return JUICE_ERR_FAILED;
}

JUICE_EXPORT juice_state_t juice_get_state(juice_agent_t *agent) { return agent_get_state(agent); }

JUICE_EXPORT int juice_get_selected_candidates(juice_agent_t *agent, char *local, size_t local_size,
                                               char *remote, size_t remote_size) {
	if (!agent || (!local && local_size) || (!remote && remote_size))
		return JUICE_ERR_INVALID;

	ice_candidate_t local_cand, remote_cand;
	if (agent_get_selected_candidate_pair(agent, &local_cand, &remote_cand))
		return JUICE_ERR_NOT_AVAIL;

	if (local_size && ice_generate_candidate_sdp(&local_cand, local, local_size) < 0)
		return JUICE_ERR_FAILED;

	if (remote_size && ice_generate_candidate_sdp(&remote_cand, remote, remote_size) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
}

static     int addstring(char *buf,int maxlen,const char *add,int len) {
        int addlen=len<maxlen?maxlen:len;
        memcpy(buf,add,addlen);
        return addlen-1;
        };
int addtype(char *buf,int maxlen,enum ice_candidate_type t) {
    switch(t) {
        case ICE_CANDIDATE_TYPE_HOST: {
            char type[]=" Host";
            return addstring(buf,maxlen,type,sizeof(type));
            }
            break;
        case ICE_CANDIDATE_TYPE_PEER_REFLEXIVE: {
            char type[]=" Peer Reflexive";
            return addstring(buf,maxlen,type,sizeof(type));
            }
            break;
        case ICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: {
            char type[]=" Server Reflexive";
            return addstring(buf,maxlen,type,sizeof(type));
            }
            break;
        case ICE_CANDIDATE_TYPE_RELAYED: {
            char type[]=" Relay";
            return addstring(buf,maxlen,type,sizeof(type));
            }
            break;
        default: {
            char type[]=" Unknown";
            return addstring(buf,maxlen,type,sizeof(type));
            }
          };
    }

JUICE_EXPORT int juice_get_selected_addresses_inc_type(juice_agent_t *agent, char *local, size_t local_size,
                                              char *remote, size_t remote_size) {
	if (!agent || (!local && local_size) || (!remote && remote_size))
		return JUICE_ERR_INVALID;

	ice_candidate_t local_cand, remote_cand;
	if (agent_get_selected_candidate_pair(agent, &local_cand, &remote_cand))
		return JUICE_ERR_NOT_AVAIL;
    if(local_size)  {
        int len;
        if((len=addr_record_to_string(&local_cand.resolved, local, local_size)) < 0)
		    return JUICE_ERR_FAILED;
        addtype(local+len,local_size-1,local_cand.type);

        }

	if(remote_size) {
        int len;
        if((len=addr_record_to_string(&remote_cand.resolved, remote, remote_size)) < 0) 
            return JUICE_ERR_FAILED;
        addtype(remote+len,remote_size-len,remote_cand.type);
        }

	return JUICE_ERR_SUCCESS;
}

JUICE_EXPORT int juice_get_selected_addresses(juice_agent_t *agent, char *local, size_t local_size,
                                              char *remote, size_t remote_size) {
	if (!agent || (!local && local_size) || (!remote && remote_size))
		return JUICE_ERR_INVALID;

	ice_candidate_t local_cand, remote_cand;
	if (agent_get_selected_candidate_pair(agent, &local_cand, &remote_cand))
		return JUICE_ERR_NOT_AVAIL;

	if (local_size && addr_record_to_string(&local_cand.resolved, local, local_size) < 0)
		return JUICE_ERR_FAILED;

	if (remote_size && addr_record_to_string(&remote_cand.resolved, remote, remote_size) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
}

int juice_set_local_ice_attributes(juice_agent_t *agent, const char *ufrag, const char *pwd)
{
	if (!ufrag || !pwd)
		return JUICE_ERR_INVALID;

	return agent_set_local_ice_attributes(agent, ufrag, pwd);
}

JUICE_EXPORT const char *juice_state_to_string(juice_state_t state) {
	switch (state) {
	case JUICE_STATE_DISCONNECTED:
		return "disconnected";
	case JUICE_STATE_GATHERING:
		return "gathering";
	case JUICE_STATE_CONNECTING:
		return "connecting";
	case JUICE_STATE_CONNECTED:
		return "connected";
	case JUICE_STATE_COMPLETED:
		return "completed";
	case JUICE_STATE_FAILED:
		return "failed";
	default:
		return "unknown";
	}
}

JUICE_EXPORT juice_server_t *juice_server_create(const juice_server_config_t *config) {
#ifndef NO_SERVER
	if (!config)
		return NULL;

	return server_create(config);
#else
	(void)config;
	JLOG_FATAL("The library was compiled without server support");
	return NULL;
#endif
}

JUICE_EXPORT void juice_server_destroy(juice_server_t *server) {
#ifndef NO_SERVER
	if (server)
		server_destroy(server);
#else
	(void)server;
#endif
}

JUICE_EXPORT uint16_t juice_server_get_port(juice_server_t *server) {
#ifndef NO_SERVER
	return server ? server_get_port(server) : 0;
#else
	(void)server;
	return 0;
#endif
}

JUICE_EXPORT int juice_server_add_credentials(juice_server_t *server,
                                              const juice_server_credentials_t *credentials,
                                              unsigned long lifetime_ms) {
#ifndef NO_SERVER
	if (!server || !credentials)
		return JUICE_ERR_INVALID;

	if (server_add_credentials(server, credentials, (timediff_t)lifetime_ms) < 0)
		return JUICE_ERR_FAILED;

	return JUICE_ERR_SUCCESS;
#else
	(void)server;
	(void)credentials;
	(void)lifetime_ms;
	return JUICE_ERR_INVALID;
#endif
}

int juice_set_ice_tcp_mode(juice_agent_t *agent, juice_ice_tcp_mode_t ice_tcp_mode)
{
	if (!agent)
		return JUICE_ERR_INVALID;

	return agent_set_ice_tcp_mode(agent, ice_tcp_mode);
}
