/* GPRS-NS type definitions in TTCN-3
 * (C) 2017 Harald Welte <laforge@gnumonks.org>
 * 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
 */

module NS_Types {
	import from General_Types all;
	import from Osmocom_Types all;
	import from GSM_Types all;
	import from BSSGP_Types all;
	import from BSSGP_Helper_Functions all;

	/* TS 48.016 10.3.7 */
	type enumerated NsPduType {
		NS_PDUT_NS_UNITDATA	('00000000'B),
		NS_PDUT_NS_RESET	('00000010'B),
		NS_PDUT_NS_RESET_ACK	('00000011'B),
		NS_PDUT_NS_BLOCK	('00000100'B),
		NS_PDUT_NS_BLOCK_ACK	('00000101'B),
		NS_PDUT_NS_UNBLOCK	('00000110'B),
		NS_PDUT_NS_UNBLOCK_ACK	('00000111'B),
		NS_PDUT_NS_STATUS	('00001000'B),
		NS_PDUT_NS_ALIVE	('00001010'B),
		NS_PDUT_NS_ALIVE_ACK	('00001011'B)
		/* FIXME: SNS */
	} with { variant "FIELDLENGTH(8)" };

	/* TS 48.016 10.3 */
	type enumerated NsIEI {
		NS_IEI_CAUSE		('00000000'B),
		NS_IEI_NSVCI		('00000001'B),
		NS_IEI_NS_PDU		('00000010'B),
		NS_IEI_BVCI		('00000011'B),
		NS_IEI_NSEI		('00000100'B),
		NS_IEI_LIST_IPv4	('00000101'B),
		NS_IEI_LIST_IPv6	('00000110'B),
		NS_IEI_MAX_NUM_NSVC	('00000111'B),
		NS_IEI_NUM_IPv4_EP	('00001000'B),
		NS_IEI_NUM_IPv6_EP	('00001001'B),
		NS_IEI_RESET_FLAG	('00001010'B),
		NS_IEI_IP_ADDRESS	('00001011'B)
	} with { variant "FIELDLENGTH(8)" };

	/* TS 48.016 10.3.2 */
	type enumerated NsCause {
		NS_CAUSE_TRANSIT_NETWORK_FAILURE		('00000000'B),
		NS_CAUSE_OM_INTERVENTION			('00000001'B),
		NS_CAUSE_EQUIPMENT_FAILURE			('00000010'B),
		NS_CAUSE_NSVC_BLOCKED				('00000011'B),
		NS_CAUSE_NSVC_UNKNOWN				('00000100'B),
		NS_CAUSE_BVCI_UNKNOWN_AT_NSE			('00000101'B),
		NS_CAUSE_SEMANTICALLY_INCORRECT_PDU		('00001000'B),
		NS_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE	('00001010'B),
		NS_CAUSE_PROTOCOL_ERROR_UNSPECIFIED		('00001011'B),
		NS_CAUSE_INVALID_ESSENTIAL_IE			('00001100'B),
		NS_CAUSE_MISSING_ESSENTIAL_IE			('00001101'B),
		NS_CAUSE_INVALID_NR_OF_IPv4_ENDPOINTS		('00001110'B),
		NS_CAUSE_INVALID_NR_OF_IPv6_ENDPOINTS		('00001111'B),
		NS_CAUSE_INVALID_NR_OF_NSVCS			('00010000'B),
		NS_CAUSE_INVALID_WEIGHTS			('00010001'B),
		NS_CAUSE_UNKNOWN_IP_ENDPOINT			('00010010'B),
		NS_CAUSE_UNKNOWN_IP_ADDRESS			('00010011'B),
		NS_CAUSE_IP_TEST_FAILEDA			('00010100'B)
	} with { variant "FIELDLENGTH(8)" };

	/* TS 48.016 10.3.9 */
	type record NsSduControlBits {
		BIT6	spare,
		boolean c,
		boolean r
	} with { variant (c) "FIELDLENGTH(1)"
		 variant (r) "FIELDLENGTH(1)"
	};

	template NsSduControlBits t_SduCtrlB := {
		spare := '000000'B,
		c := false,
		r := false
	}

	type uint16_t Nsvci;
	type uint16_t Nsei;

	type union NsIeUnion {
		BssgpBvci		bvci,		/* 10.3.1 */
		NsCause			cause,		/* 10.3.2 */
		uint16_t		max_num_nsvc,	/* 10.3.2e */
		uint16_t		num_ipv4_ep,	/* 10.3.2f */
		uint16_t		num_ipv6_ep,	/* 10.3.2g */
		Nsvci			nsvci,		/* 10.3.5 */
		Nsei			nsei,		/* 10.3.6 */
		octetstring 		other
	};

	type record NsTLV {
		NsIEI		iei,
		uint16_t	len,
		NsIeUnion	u
	} with {
		variant (u) "CROSSTAG(
				bvci, 			iei = NS_IEI_BVCI;
				cause,			iei = NS_IEI_CAUSE;
				max_num_nsvc,		iei = NS_IEI_MAX_NUM_NSVC;
				num_ipv4_ep,		iei = NS_IEI_NUM_IPv4_EP;
				num_ipv6_ep,		iei = NS_IEI_NUM_IPv6_EP;
				nsvci,			iei = NS_IEI_NSVCI;
				nsei,			iei = NS_IEI_NSEI;
				other,			OTHERWISE)"
		variant (len) "LENGTHTO(u)"
	};

	type record of NsTLV NsTLVs;

	type record NsPduUnitdata {
		NsSduControlBits	control_bits,
		BssgpBvci		bvci,
		octetstring		sdu
	} with { variant "" };

	type record NsPduOther {
		NsTLVs		tlvs optional
	} with { variant "" };

	type union NsPduUnion {
		NsPduUnitdata	unitdata,
		NsPduOther	other
	} with { variant "" };

	type record NsPdu {
		NsPduType	pdu_type,
		NsPduUnion	u
	} with { variant (u) "CROSSTAG(
				unitdata,		pdu_type = NS_PDUT_NS_UNITDATA;
				other,			OTHERWISE)"
	};

	external function enc_NsPdu(in NsPdu pdu) return octetstring
		with { extension "prototype(convert) encode(RAW)" };
	external function dec_NsPdu(in octetstring stream) return NsPdu
		with { extension "prototype(convert) decode(RAW)" };



	template NsTLV t_NS_IE_CAUSE(template NsCause cause) := {
		iei := NS_IEI_CAUSE,
		len := 1,
		u := { cause := cause }
	};

	template NsTLV t_NS_IE_NSVCI(template Nsvci nsvci) := {
		iei := NS_IEI_NSVCI,
		len := 2,
		u := { nsvci :=  nsvci }
	}

	template NsTLV t_NS_IE_NSEI(template Nsvci nsei) := {
		iei := NS_IEI_NSEI,
		len := 2,
		u := { nsei := nsei }
	}

	template NsTLV t_NsIE(NsIEI iei, NsIeUnion u) := {
		iei := iei,
		u := u
	}

	template NsTLV t_NsIE_other(NsIEI iei, octetstring val) := {
		iei := iei,
		len := lengthof(val),
		u := { other := val }
	}

	template NsPdu t_NS_RESET(template NsCause cause, template Nsvci nsvci, template Nsei nsei) := {
		pdu_type := NS_PDUT_NS_RESET,
		u := {
			other := {
				tlvs := { t_NS_IE_CAUSE(cause), t_NS_IE_NSVCI(nsvci), t_NS_IE_NSEI(nsei) }
			}
		}
	};

	template NsPdu t_NS_RESET_ACK(template Nsvci nsvci, template Nsei nsei) := {
		pdu_type := NS_PDUT_NS_RESET_ACK,
		u := {
			other := {
				tlvs := { t_NS_IE_NSVCI(nsvci), t_NS_IE_NSEI(nsei) }
			}
		}
	};

	template NsPdu t_NS_BLOCK(template NsCause cause, template Nsvci nsvci) := {
		pdu_type := NS_PDUT_NS_BLOCK,
		u := {
			other := {
				tlvs := { t_NS_IE_CAUSE(cause), t_NS_IE_NSVCI(nsvci) }
			}
		}
	}

	template NsPdu t_NS_BLOCK_ACK(template Nsvci nsvci) := {
		pdu_type := NS_PDUT_NS_BLOCK_ACK,
		u := {
			other := {
				tlvs := { t_NS_IE_NSVCI(nsvci) }
			}
		}
	}

	template NsPdu t_NsPduSimple(template NsPduType pdut) := { pdu_type := pdut, u := { other := { tlvs := omit } } };
	template NsPdu t_NS_ALIVE := t_NsPduSimple(NS_PDUT_NS_ALIVE);
	template NsPdu t_NS_ALIVE_ACK := t_NsPduSimple(NS_PDUT_NS_ALIVE_ACK);
	template NsPdu t_NS_UNBLOCK := t_NsPduSimple(NS_PDUT_NS_UNBLOCK);
	template NsPdu t_NS_UNBLOCK_ACK := t_NsPduSimple(NS_PDUT_NS_UNBLOCK_ACK);

	template NsPdu t_NS_STATUS(NsCause cause, NsPdu pdu) := {
		pdu_type := NS_PDUT_NS_STATUS,
		u := {
			other := {
				tlvs := { t_NS_IE_CAUSE(cause), t_NsIE_other(NS_IEI_NS_PDU, f_NS_compact_len(enc_NsPdu(pdu))) }
			}
		}
	}

	template NsPdu t_NS_UNITDATA(template NsSduControlBits bits, template BssgpBvci bvci, template octetstring sdu) := {
		pdu_type := NS_PDUT_NS_UNITDATA,
		u := {
			unitdata := {
				control_bits := bits,
				bvci := bvci,
				sdu := sdu
			}
		}
	}

} with { encode "RAW" };
