/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of mediastreamer2
 * (see https://gitlab.linphone.org/BC/public/mediastreamer2).
 *
 * 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 <bctoolbox/defs.h>

#include "mediastreamer2/bitratecontrol.h"

static const int probing_up_interval = 10;

enum state_t { Init, Probing, Stable, ProbingUp };

const char *state_name(enum state_t st) {
	switch (st) {
		case Init:
			return "Init";
		case Probing:
			return "Probing";
		case Stable:
			return "Stable";
		case ProbingUp:
			return "ProbingUp";
	}
	return "bad state";
}

struct _MSBitrateController {
	MSQosAnalyzer *analyzer;
	MSBitrateDriver *driver;
	enum state_t state;
	int stable_count;
	int probing_up_count;
};

MSBitrateController *ms_bitrate_controller_new(MSQosAnalyzer *qosanalyzer, MSBitrateDriver *driver) {
	MSBitrateController *obj = ms_new0(MSBitrateController, 1);
	obj->analyzer = ms_qos_analyzer_ref(qosanalyzer);
	obj->driver = ms_bitrate_driver_ref(driver);
	return obj;
}

static int execute_action(MSBitrateController *obj, const MSRateControlAction *action) {
	return ms_bitrate_driver_execute_action(obj->driver, action);
}

static void state_machine(MSBitrateController *obj) {
	MSRateControlAction action = {0};
	switch (obj->state) {
		case Stable:
			obj->stable_count++;
			BCTBX_NO_BREAK; /*intentionally no break*/
		case Init:
			ms_qos_analyzer_suggest_action(obj->analyzer, &action);
			if (action.type != MSRateControlActionDoNothing) {
				execute_action(obj, &action);
				obj->state = Probing;
			} else if (obj->stable_count >= probing_up_interval) {
				action.type = MSRateControlActionIncreaseQuality;
				action.value = 10;
				execute_action(obj, &action);
				obj->state = ProbingUp;
				obj->probing_up_count = 0;
			}
			break;
		case Probing:
			obj->stable_count = 0;
			if (ms_qos_analyzer_has_improved(obj->analyzer)) {
				obj->state = Stable;
			} else {
				ms_qos_analyzer_suggest_action(obj->analyzer, &action);
				if (action.type != MSRateControlActionDoNothing) {
					execute_action(obj, &action);
				}
			}
			break;
		case ProbingUp:
			obj->stable_count = 0;
			obj->probing_up_count++;
			ms_qos_analyzer_suggest_action(obj->analyzer, &action);
			if (action.type != MSRateControlActionDoNothing) {
				execute_action(obj, &action);
				obj->state = Probing;
			} else {
				/*continue with slow ramp up*/
				if (obj->probing_up_count == 2) {
					action.type = MSRateControlActionIncreaseQuality;
					action.value = 10;
					if (execute_action(obj, &action) == -1) {
						/* we reached the maximum*/
						obj->state = Init;
					}
					obj->probing_up_count = 0;
				}
			}
			break;
		default:
			break;
	}
	ms_message("MSBitrateController: current state is %s", state_name(obj->state));
}

void ms_bitrate_controller_process_rtcp(MSBitrateController *obj, const mblk_t *rtcp) {
	if (ms_qos_analyzer_process_rtcp(obj->analyzer, rtcp)) {
		state_machine(obj);
	}
}

void ms_bitrate_controller_update(MSBitrateController *obj) {
	ms_qos_analyzer_update(obj->analyzer);
}

MSQosAnalyzer *ms_bitrate_controller_get_qos_analyzer(MSBitrateController *obj) {
	return obj->analyzer;
}

void ms_bitrate_controller_destroy(MSBitrateController *obj) {
	ms_qos_analyzer_unref(obj->analyzer);
	ms_bitrate_driver_unref(obj->driver);
	ms_free(obj);
}

MSBitrateController *
ms_audio_bitrate_controller_new(RtpSession *session, MSFilter *encoder, BCTBX_UNUSED(unsigned int flags)) {
	return ms_bitrate_controller_new(ms_simple_qos_analyzer_new(session),
	                                 ms_audio_bitrate_driver_new(session, encoder));
}

MSBitrateController *
ms_av_bitrate_controller_new(RtpSession *asession, MSFilter *aenc, RtpSession *vsession, MSFilter *venc) {
	return ms_bitrate_controller_new(ms_simple_qos_analyzer_new(vsession),
	                                 ms_av_bitrate_driver_new(asession, aenc, vsession, venc));
}

MSBitrateController *
ms_bandwidth_bitrate_controller_new(RtpSession *asession, MSFilter *aenc, RtpSession *vsession, MSFilter *venc) {
	return ms_bitrate_controller_new(ms_stateful_qos_analyzer_new(vsession ? vsession : asession),
	                                 ms_bandwidth_bitrate_driver_new(asession, aenc, vsession, venc));
}
