/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone
 * (see https://gitlab.linphone.org/BC/public/liblinphone).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "netsim.h"

using namespace std;

class NetsimResponse : public Response {
public:
	NetsimResponse(LinphoneCore *core);
};

NetsimResponse::NetsimResponse(LinphoneCore *lc) : Response() {
	ostringstream ost;
	const OrtpNetworkSimulatorParams *params = linphone_core_get_network_simulator_params(lc);
	ost << "State: ";
	if (params->enabled) {
		ost << "enabled\n";
	} else {
		ost << "disabled\n";
	}
	ost << "max_bandwidth: " << params->max_bandwidth << endl;
	ost << "max_buffer_size: " << params->max_buffer_size << endl;
	ost << "loss_rate: " << params->loss_rate << endl;
	ost << "latency: " << params->latency << endl;
	ost << "consecutive_loss_probability: " << params->consecutive_loss_probability << endl;
	ost << "jitter_burst_density: " << params->jitter_burst_density << endl;
	ost << "jitter_strength: " << params->jitter_strength << endl;
	ost << "mode: " << ortp_network_simulator_mode_to_string(params->mode) << endl;
	setBody(ost.str());
}

NetsimCommand::NetsimCommand()
    : DaemonCommand("netsim",
                    "netsim [enable|disable|parameters] [<parameters>]",
                    "Configure the network simulator. Parameters are to be provided in the form "
                    "param-name=param-value, separated with ';' only. Supported parameters are:\n"
                    "\tmax_bandwidth (kbit/s)\n"
                    "\tmax_buffer_size (bits)\n"
                    "\tloss_rate (percentage)\n"
                    "\tlatency (milliseconds)\n"
                    "\tconsecutive_loss_probability (0..1)\n"
                    "\tjitter_burst_density (frequency of bursts 0..1)\n"
                    "\tjitter_strength (percentage of max_bandwidth artifically consumed during bursts events)\n"
                    "\tmode (inbound, outbound, outbound-controlled)\n") {
	addExample(make_unique<DaemonCommandExample>(
	    "netsim", "Status: Ok\n\n"
	              "State: disabled\nmax_bandwidth: 384000\nmax_buffer_size: 384000\nloss_rate: 2"));
	addExample(make_unique<DaemonCommandExample>(
	    "netsim enable", "Status: Ok\n\n"
	                     "State: enabled\nmax_bandwidth: 384000\nmax_buffer_size: 384000\nloss_rate: 2"));
	addExample(make_unique<DaemonCommandExample>(
	    "netsim parameters loss_rate=5;consecutive_loss_probability=0.5",
	    "Status: Ok\n\n"
	    "State: enabled\nmax_bandwidth: 384000\nmax_buffer_size: 384000\nloss_rate: 2"));
}

void NetsimCommand::exec(Daemon *app, const string &args) {
	LinphoneCore *lc = app->getCore();
	OrtpNetworkSimulatorParams params = *linphone_core_get_network_simulator_params(lc);
	string subcommand;
	istringstream ist(args);
	ist >> subcommand;
	if (ist.fail()) {
		app->sendResponse(NetsimResponse(app->getCore()));
		return;
	}
	if (subcommand.compare("enable") == 0) {
		params.enabled = TRUE;
	} else if (subcommand.compare("disable") == 0) {
		params.enabled = FALSE;
	} else if (subcommand.compare("parameters") == 0) {
		string parameters;
		char value[128] = {0};
		ist >> parameters;
		if (fmtp_get_value(parameters.c_str(), "max_bandwidth", value, sizeof(value))) {
			params.max_bandwidth = (float)atoi(value);
		}
		if (fmtp_get_value(parameters.c_str(), "max_buffer_size", value, sizeof(value))) {
			params.max_buffer_size = atoi(value);
		}
		if (fmtp_get_value(parameters.c_str(), "loss_rate", value, sizeof(value))) {
			params.loss_rate = (float)atoi(value);
		}
		if (fmtp_get_value(parameters.c_str(), "latency", value, sizeof(value))) {
			params.latency = (uint32_t)atoi(value);
		}
		if (fmtp_get_value(parameters.c_str(), "consecutive_loss_probability", value, sizeof(value))) {
			params.consecutive_loss_probability = (float)atof(value);
		}
		if (fmtp_get_value(parameters.c_str(), "jitter_burst_density", value, sizeof(value))) {
			params.jitter_burst_density = (float)atof(value);
		}
		if (fmtp_get_value(parameters.c_str(), "jitter_strength", value, sizeof(value))) {
			params.jitter_strength = (float)atof(value);
		}
		if (fmtp_get_value(parameters.c_str(), "mode", value, sizeof(value))) {
			OrtpNetworkSimulatorMode mode = ortp_network_simulator_mode_from_string(value);
			if (mode == OrtpNetworkSimulatorInvalid) {
				app->sendResponse(Response("Invalid mode"));
				return;
			}
			params.mode = mode;
		}
	}
	linphone_core_set_network_simulator_params(lc, &params);
	app->sendResponse(NetsimResponse(app->getCore()));
}
