/*
 * Copyright (c) 2010-2019 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "port.h"

using namespace std;

enum Protocol {
	UDPProtocol = 0,
	TCPProtocol = 1,
	TLSProtocol = 2
};

class PortResponse : public Response {
public:
	enum PortType {
		SIPPort = 0,
		AudioRTPPort = 1,
		VideoRTPPort = 2,
		AllPorts = 3
	};

	PortResponse(LinphoneCore *core, PortType type);

private:
	void outputSIPPort(LinphoneCore *core, ostringstream &ost);
	void outputAudioRTPPort(LinphoneCore *core, ostringstream &ost);
	void outputVideoRTPPort(LinphoneCore *core, ostringstream &ost);
};

PortResponse::PortResponse(LinphoneCore *core, PortResponse::PortType type) : Response() {
	ostringstream ost;
	switch (type) {
		case SIPPort:
			outputSIPPort(core, ost);
			break;
		case AudioRTPPort:
			outputAudioRTPPort(core, ost);
			break;
		case VideoRTPPort:
			outputVideoRTPPort(core, ost);
			break;
		case AllPorts:
			outputSIPPort(core, ost);
			outputAudioRTPPort(core, ost);
			outputVideoRTPPort(core, ost);
			break;
	}
	setBody(ost.str());
}

void PortResponse::outputSIPPort(LinphoneCore *core, ostringstream &ost) {
	LCSipTransports transports;
	linphone_core_get_sip_transports(core, &transports);
	ost << "SIP: ";
	if (transports.udp_port > 0) {
		ost << transports.udp_port << " UDP\n";
	} else if (transports.tcp_port > 0) {
		ost << transports.tcp_port << " TCP\n";
	} else {
		ost << transports.tls_port << " TLS\n";
	}
}

void PortResponse::outputAudioRTPPort(LinphoneCore *core, ostringstream &ost) {
	ost << "Audio RTP: " << linphone_core_get_audio_port(core) << "\n";
}

void PortResponse::outputVideoRTPPort(LinphoneCore *core, ostringstream &ost) {
	ost << "Video RTP: " << linphone_core_get_video_port(core) << "\n";
}

PortCommand::PortCommand() :
		DaemonCommand("port", "port [sip|audio|video] [<port>] [udp|tcp|tls]",
				"Set the port to use for type if port is set, otherwise return the port used for type if specified or all the used ports if no type is specified.\n"
				"The protocol should be defined only for sip port and have one of these values: udp, tcp, tls.") {
	addExample(make_unique<DaemonCommandExample>("port sip 5060 tls",
						"Status: Ok\n\n"
						"SIP: 5060 TLS"));
	addExample(make_unique<DaemonCommandExample>("port sip 5060 udp",
						"Status: Ok\n\n"
						"SIP: 5060 UDP"));
	addExample(make_unique<DaemonCommandExample>("port audio 7078",
						"Status: Ok\n\n"
						"Audio RTP: 7078"));
	addExample(make_unique<DaemonCommandExample>("port video 9078",
						"Status: Ok\n\n"
						"Video RTP: 9078"));
	addExample(make_unique<DaemonCommandExample>("port sip",
						"Status: Ok\n\n"
						"SIP: 5060 UDP"));
	addExample(make_unique<DaemonCommandExample>("port audio",
						"Status: Ok\n\n"
						"Audio RTP: 7078"));
	addExample(make_unique<DaemonCommandExample>("port",
						"Status: Ok\n\n"
						"SIP: 5060 UDP\n"
						"Audio RTP: 7078\n"
						"Video RTP: 9078"));
}

void PortCommand::exec(Daemon *app, const string& args) {
	string type;
	int port;
	istringstream ist(args);
	ostringstream ost;
	ist >> type;
	if (ist.eof() && (type.length() == 0)) {
		app->sendResponse(PortResponse(app->getCore(), PortResponse::AllPorts));
		return;
	}
	if (ist.fail()) {
		app->sendResponse(Response("Incorrect type parameter.", Response::Error));
		return;
	}
	ist >> port;
	if (ist.fail()) {
		if (type.compare("sip") == 0) {
			app->sendResponse(PortResponse(app->getCore(), PortResponse::SIPPort));
		} else if (type.compare("audio") == 0) {
			app->sendResponse(PortResponse(app->getCore(), PortResponse::AudioRTPPort));
		} else if (type.compare("video") == 0) {
			app->sendResponse(PortResponse(app->getCore(), PortResponse::VideoRTPPort));
		} else {
			app->sendResponse(Response("Incorrect type parameter.", Response::Error));
		}
		return;
	}
	if (type.compare("sip") == 0) {
		Protocol protocol = UDPProtocol;
		string protocol_str;
		ist >> protocol_str;
		if (!ist.fail()) {
			if (protocol_str.compare("udp") == 0) {
				protocol = UDPProtocol;
			} else if (protocol_str.compare("tcp") == 0) {
				protocol = TCPProtocol;
			} else if (protocol_str.compare("tls") == 0) {
				protocol = TLSProtocol;
			} else {
				app->sendResponse(Response("Incorrect protocol parameter.", Response::Error));
				return;
			}
		}
		LCSipTransports transports;
		memset(&transports, 0, sizeof(transports));
		switch (protocol) {
			case UDPProtocol:
				transports.udp_port = port;
				break;
			case TCPProtocol:
				transports.tcp_port = port;
				break;
			case TLSProtocol:
				transports.tls_port = port;
				break;
		}
		linphone_core_set_sip_transports(app->getCore(), &transports);
		app->sendResponse(PortResponse(app->getCore(), PortResponse::SIPPort));
	} else if (type.compare("audio") == 0) {
		linphone_core_set_audio_port(app->getCore(), port);
		app->sendResponse(PortResponse(app->getCore(), PortResponse::AudioRTPPort));
	} else if (type.compare("video") == 0) {
		linphone_core_set_video_port(app->getCore(), port);
		app->sendResponse(PortResponse(app->getCore(), PortResponse::VideoRTPPort));
	} else {
		app->sendResponse(Response("Incorrect type parameter.", Response::Error));
	}
}
