module BTS_Tests_VAMOS {

/* Integration Tests for OsmoBTS
 *
 * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
 *
 * All rights reserved.
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * This module contains VAMOS specific test cases.
 */

import from Misc_Helpers all;
import from General_Types all;
import from Osmocom_Types all;
import from GSM_Types all;
import from L1CTL_PortType all;
import from L1CTL_Types all;
import from LAPDm_Types all;
import from IPA_Emulation all;
import from GSM_RR_Types all;
import from L3_Templates all;

import from RSL_Emulation all;
import from RSL_Types all;

import from BTS_Tests all;

private type record of ChanNrModeSet ChanNrModeTest;
private type record of ChanNrMode ChanNrModeSet;
private type record ChanNrMode {
	RslChannelNr chan_nr,
	RSL_IE_ChannelMode chan_mode
};

/* VFF: V0(TCH/F) & V1(TCH/F), 2 channels total */
private template (value) ChanNrModeTest
ChanNrModeTestVFF(uint3_t tn) := {
	{
		{ ts_RslChanNr_Bm(tn), /* V0(TCH/F), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_F_VAMOS) },
		{ ts_RslChanNr_Osmo_VAMOS_Bm(tn), /* V1(TCH/F), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_F_VAMOS) }
	},
	{
		{ ts_RslChanNr_Bm(tn), /* V0(TCH/F), FR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_F_VAMOS, RSL_CMOD_SP_GSM1) },
		{ ts_RslChanNr_Osmo_VAMOS_Bm(tn), /* V1(TCH/F), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_F_VAMOS) }
	},
	{
		{ ts_RslChanNr_Bm(tn), /* V0(TCH/F), EFR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_F_VAMOS, RSL_CMOD_SP_GSM2) },
		{ ts_RslChanNr_Osmo_VAMOS_Bm(tn), /* V1(TCH/F), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_F_VAMOS, RSL_CMOD_SP_GSM3) }
	},
	{
		{ ts_RslChanNr_Bm(tn), /* V0(TCH/F), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_F_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Osmo_VAMOS_Bm(tn), /* V1(TCH/F), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_F_VAMOS, RSL_CMOD_SP_GSM3) }
	}
};

/* VHH: V0(TCH/H0) & V1(TCH/H0) + V0(TCH/H1) & V1(TCH/H1), 4 channels total */
private template (value) ChanNrModeTest
ChanNrModeTestVHH(uint3_t tn) := {
	{
		{ ts_RslChanNr_Lm(tn, 0), /* V0(TCH/H0), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 0), /* V1(TCH/H0), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) }
	},
	{
		{ ts_RslChanNr_Lm(tn, 0), /* V0(TCH/H0), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), HR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM1) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 0), /* V1(TCH/H0), HR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM1) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) }
	},
	{
		{ ts_RslChanNr_Lm(tn, 0), /* V0(TCH/H0), HR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM1) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 0), /* V1(TCH/H0), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) }
	},
	{
		{ ts_RslChanNr_Lm(tn, 0), /* V0(TCH/H0), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 0), /* V1(TCH/H0), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) }
	}
};

/* HVHH: TCH/H0 + V0(TCH/H1) & V1(TCH/H1), 3 channels total (mixed) */
private template (value) ChanNrModeTest
ChanNrModeTestHVHH(uint3_t tn) := {
	{
		{ ts_RslChanNr_Lm(tn, 0), /* TCH/H0, signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_TCH_H) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_OSMO_TCH_H_VAMOS) }
	},
	{
		{ ts_RslChanNr_Lm(tn, 0), /* TCH/H0, signalling */
		  ts_RSL_ChanMode_SIGN(RSL_CHRT_TCH_H) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) }
	},
	{
		{ ts_RslChanNr_Lm(tn, 0), /* TCH/H0, HR codec */
		  ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), HR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM1) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) }
	},
	{
		{ ts_RslChanNr_Lm(tn, 0), /* TCH/H0, AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Lm(tn, 1), /* V0(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) },
		{ ts_RslChanNr_Osmo_VAMOS_Lm(tn, 1), /* V1(TCH/H1), AMR codec */
		  ts_RSL_ChanMode(RSL_CHRT_OSMO_TCH_H_VAMOS, RSL_CMOD_SP_GSM3) }
	}
};

private template (value) RSL_IE
ts_RSL_IE_OsmoTSC := {
	iei := RSL_IE_OSMO_TRAINING_SEQUENCE,
	body := {
		osmo_training_sequence := {
			len := 0, /* overridden */
			tsc_set := f_rnd_int(4),
			tsc := f_rnd_int(8)
		}
	}
}

private function f_TC_vamos_exec_async(in ChanNrModeTest test, void_fn handler)
runs on test_CT {
	/* Up to 4 simultenious lchans */
	var ConnHdlrPars pars[4];
	var ConnHdlr vc_conn[4];

	f_init();

	for (var integer i := 0; i < lengthof(test); i := i + 1) {
		/* Start a ConnHdlr component for each logical channel */
		for (var integer ch := 0; ch < lengthof(test[i]); ch := ch + 1) {
			pars[ch] := valueof(t_Pars(test[i][ch].chan_nr,
						   test[i][ch].chan_mode));
			if (ischosen(test[i][ch].chan_mode.u.speech) and test[i][ch].chan_mode.u.speech == RSL_CMOD_SP_GSM3) {
				pars[ch].mr_conf := valueof(ts_RSL_MultirateCfg);
			}
			vc_conn[ch] := f_start_handler(handler, pars[ch], l1ctl := false);
		}

		/* Wait for all ConnHdlr components to finish */
		for (var integer ch := 0; ch < lengthof(test[i]); ch := ch + 1) {
			vc_conn[ch].done;
		}
	}

	Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}

private function f_TC_vamos_chan_act(charstring id)
runs on ConnHdlr {
	var RSL_IE tsc_ie := valueof(ts_RSL_IE_OsmoTSC);

	/* CHANnel ACTIVation with Osmocom specific TSC IE */
	f_rsl_chan_act(g_pars.chan_mode, more_ies := { tsc_ie });

	/* Hold the channel for a while */
	f_sleep(0.3);

	/* DEACTivate the channel */
	f_rsl_chan_deact();
	f_rslem_unregister(0, g_chan_nr);
}

/* VFF: V0(TCH/F) & V1(TCH/F), 2 channels total */
testcase TC_vamos_chan_act_vff() runs on test_CT { /* TCH/F on TS1 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVFF(1));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act));
}
testcase TC_vamos_chan_act_dyn_ipa_vff() runs on test_CT { /* TCH/F+PDCH on TS3 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVFF(3));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act));
}
testcase TC_vamos_chan_act_dyn_osmo_vff() runs on test_CT { /* TCH/F+TCH/H+PDCH on TS4 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVFF(4));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act));
}

/* VHH: V0(TCH/H0) & V1(TCH/H0) + V0(TCH/H1) & V1(TCH/H1), 4 channels total */
testcase TC_vamos_chan_act_vhh() runs on test_CT {
	var ChanNrModeTest test := valueof(ChanNrModeTestVHH(5));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act)); /* TCH/H on TS5 */
}
testcase TC_vamos_chan_act_dyn_osmo_vhh() runs on test_CT { /* TCH/F+TCH/H+PDCH on TS4 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVHH(4));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act));
}

/* HVHH: TCH/H0 + V0(TCH/H1) & V1(TCH/H1), 3 channels total (mixed) */
testcase TC_vamos_chan_act_hvhh() runs on test_CT {
	var ChanNrModeTest test := valueof(ChanNrModeTestHVHH(5));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act)); /* TCH/H on TS5 */
}
testcase TC_vamos_chan_act_dyn_osmo_hvhh() runs on test_CT { /* TCH/F+TCH/H+PDCH on TS4 */
	var ChanNrModeTest test := valueof(ChanNrModeTestHVHH(4));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_act));
}

private function f_TC_vamos_chan_mode_modify(charstring id)
runs on ConnHdlr {
	var RSL_IE tsc_ie := valueof(ts_RSL_IE_OsmoTSC);

	/* CHANnel ACTIVation with Osmocom specific IEs */
	f_rsl_chan_act(g_pars.chan_mode, more_ies := { tsc_ie });

	/* If we're in signalling mode, modify to speech */
	if (g_pars.chan_mode.spd_ind == RSL_SPDI_SIGN) {
		g_pars.chan_mode.spd_ind := RSL_SPDI_SPEECH;
		g_pars.chan_mode.u.speech := RSL_CMOD_SP_GSM1;
	} else { /* ... or vice versa */
		g_pars.chan_mode.spd_ind := RSL_SPDI_SIGN;
		g_pars.chan_mode.u.sign := RSL_CMOD_NO_RESOURCE;
	}

	var RSL_Message rsl := valueof(ts_RSL_MODE_MODIFY_REQ(g_chan_nr, g_pars.chan_mode));
	rsl.ies := rsl.ies & { tsc_ie };
	RSL.send(rsl);

	timer T := 1.0;
	T.start;
	alt {
	[] RSL.receive(tr_RSL_MODE_MODIFY_ACK(g_chan_nr)) { setverdict(pass); }
	[] RSL.receive(tr_RSL_MODE_MODIFY_NACK(g_chan_nr, ?)) {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
					"Rx MODE MODIFY NACK");
		}
	[] T.timeout {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
					"Timeout waiting for MODE MODIFY (N)ACK");
		}
	}

	/* DEACTivate the channel */
	f_rsl_chan_deact();
	f_rslem_unregister(0, g_chan_nr);
}

/* VFF: V0(TCH/F) & V1(TCH/F), 2 channels total */
testcase TC_vamos_chan_mode_modify_vff() runs on test_CT { /* TCH/F on TS1 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVFF(1));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify));
}
testcase TC_vamos_chan_mode_modify_dyn_ipa_vff() runs on test_CT { /* TCH/F+PDCH on TS3 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVFF(3));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify));
}
testcase TC_vamos_chan_mode_modify_dyn_osmo_vff() runs on test_CT { /* TCH/F+TCH/H+PDCH on TS4 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVFF(4));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify));
}

/* VHH: V0(TCH/H0) & V1(TCH/H0) + V0(TCH/H1) & V1(TCH/H1), 4 channels total */
testcase TC_vamos_chan_mode_modify_vhh() runs on test_CT {
	var ChanNrModeTest test := valueof(ChanNrModeTestVHH(5));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify)); /* TCH/H on TS5 */
}
testcase TC_vamos_chan_mode_modify_dyn_osmo_vhh() runs on test_CT { /* TCH/F+TCH/H+PDCH on TS4 */
	var ChanNrModeTest test := valueof(ChanNrModeTestVHH(4));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify));
}

/* HVHH: TCH/H0 + V0(TCH/H1) & V1(TCH/H1), 3 channels total (mixed) */
testcase TC_vamos_chan_mode_modify_hvhh() runs on test_CT {
	var ChanNrModeTest test := valueof(ChanNrModeTestHVHH(5));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify)); /* TCH/H on TS5 */
}
testcase TC_vamos_chan_mode_modify_dyn_osmo_hvhh() runs on test_CT { /* TCH/F+TCH/H+PDCH on TS4 */
	var ChanNrModeTest test := valueof(ChanNrModeTestHVHH(4));
	f_TC_vamos_exec_async(test, refers(f_TC_vamos_chan_mode_modify));
}

control {
	/* CHANnel ACTIVation tests */
	execute( TC_vamos_chan_act_vff() );
	execute( TC_vamos_chan_act_vhh() );
	execute( TC_vamos_chan_act_hvhh() );
	execute( TC_vamos_chan_act_dyn_ipa_vff() );
	execute( TC_vamos_chan_act_dyn_osmo_vff() );
	execute( TC_vamos_chan_act_dyn_osmo_vhh() );
	execute( TC_vamos_chan_act_dyn_osmo_hvhh() );

	/* MODE MODIFY tests */
	execute( TC_vamos_chan_mode_modify_vff() );
	execute( TC_vamos_chan_mode_modify_vhh() );
	execute( TC_vamos_chan_mode_modify_hvhh() );
	execute( TC_vamos_chan_mode_modify_dyn_ipa_vff() );
	execute( TC_vamos_chan_mode_modify_dyn_osmo_vff() );
	execute( TC_vamos_chan_mode_modify_dyn_osmo_vhh() );
	execute( TC_vamos_chan_mode_modify_dyn_osmo_hvhh() );
}

}