module SIP_Templates {

import from SIPmsg_Types all;

/* wrapper type to encapsulate the Addr_Union + parameter list used in From, To. ... */
type record SipAddr {
	Addr_Union 		addr,
	SemicolonParam_List	params optional
}

const charstring c_SIP_VERSION := "SIP/2.0";

template (value) SipUrl ts_SipUrl(charstring user_or_tel, charstring host, integer portnr) := {
	scheme := "sip",
	userInfo := {
		userOrTelephoneSubscriber := user_or_tel,
		password := omit
	},
	hostPort := {
		host := host,
		portField := portnr
	},
	urlParameters := omit,
	headers := omit
}
template SipUrl tr_SipUrl(template charstring user_or_tel,
			  template charstring host,
			  template integer portnr) := {
	scheme := "sip",
	userInfo := {
		userOrTelephoneSubscriber := user_or_tel,
		password := *
	},
	hostPort := {
		host := host,
		portField := portnr
	},
	urlParameters := *,
	headers := *
}

template (value) SipAddr ts_SipAddr(charstring user_or_tel, charstring host, integer portnr) := {
	addr := {
		nameAddr := {
			displayName := omit,
			addrSpec := ts_SipUrl(user_or_tel, host, portnr)
		}
	},
	params := omit
}
template SipAddr tr_SipAddr(template charstring user_or_tel,
				template charstring host,
				template integer portnr) := {
	addr := {
		nameAddr := {
			displayName := *,
			addrSpec := tr_SipUrl(user_or_tel, host, portnr)
		}
	},
	params := *
}

/* build a receive template from a value: substitute '*' for omit */
function tr_SipAddr_from_val(SipAddr tin) return template SipAddr {
	var template SipAddr ret := tin;
	if (tin.addr.nameAddr.displayName == omit) {
		ret.addr.nameAddr.displayName := *;
	}
	if (tin.addr.nameAddr.addrSpec.userInfo.password == omit) {
		ret.addr.nameAddr.addrSpec.userInfo.password := *;
	}
	if (tin.params == omit) {
		ret.params := *;
	}
	return ret;
}


function tr_HostPort(template HostPort hp) return template HostPort {
	var template HostPort hpout := hp;
	/* if the port number is 5060, it may be omitted */
	if (isvalue(hp.portField) and valueof(hp.portField) == 5060) {
		hpout.portField := 5060 ifpresent;
	}
	return hpout;
}

template (value) RequestLine ts_SIP_ReqLine(Method method, template (value) SipUrl uri,
					    charstring ver := c_SIP_VERSION) := {
	method := method,
	requestUri := uri,
	sipVersion := ver
}
template RequestLine tr_SIP_ReqLine(template Method method,
				    template SipUrl uri,
				    template charstring ver := c_SIP_VERSION) := {
	method := method,
	requestUri := uri,
	sipVersion := ver
}

template (value) StatusLine ts_SIP_StatusLine(integer status_code, charstring reason) := {
	sipVersion := "SIP/2.0",
	statusCode := status_code,
	reasonPhrase := reason
}
template StatusLine tr_SIP_StatusLine(template integer status_code, template charstring reason) := {
	sipVersion := "SIP/2.0",
	statusCode := status_code,
	reasonPhrase := reason
}


template (value) PDU_SIP_Request ts_SIP_req(template (value) RequestLine rl) := {
	requestLine := rl,
	msgHeader := c_SIP_msgHeader_empty,
	messageBody := omit,
	payload := omit
}

const Method_List c_SIP_defaultMethods := {
	"INVITE", "ACK", "BYE", "CANCEL", "OPTIONS", "PRACK", "MESSAGE", "SUBSCRIBE",
	"NOTIFY", "REFER", "UPDATE" };

private function f_ContentTypeOrOmit(template (omit) ContentType ct, template (omit) charstring body)
return template (omit) ContentType {
	/* if user explicitly stated no content type */
	if (istemplatekind(ct, "omit")) {
		return omit;
	}
	/* if there's no body, then there's no content-type either */
	if (istemplatekind(body, "omit")) {
		return omit;
	}
	return ct;
}

template (value) ContentType ts_CT_SDP := {
	fieldName := CONTENT_TYPE_E,
	mediaType := "application/sdp"
};

template (value) Via ts_Via_from(SipAddr from_addr) := {
	fieldName := VIA_E,
	viaBody := {
		{
			sentProtocol := { "SIP", "2.0", "UDP" },
			sentBy := from_addr.addr.nameAddr.addrSpec.hostPort,
			viaParams := omit
		}
	}
}

template (value) MessageHeader ts_SIP_msgHeader_empty :=c_SIP_msgHeader_empty;
template (value) MessageHeader ts_SIP_msgh_std( CallidString call_id,
						SipAddr from_addr,
						SipAddr to_addr,
						template (omit) SipAddr contact_addr,
						charstring method,
						integer seq_nr,
						template (value) Via via,
						template (omit) ContentType content_type := omit,
						Method_List allow_methods := c_SIP_defaultMethods
					) modifies ts_SIP_msgHeader_empty := {
	allow := {
		fieldName := ALLOW_E,
		methods := allow_methods
	},
	callId := {
		fieldName := CALL_ID_E,
		callid := call_id
	},
	contact := ts_Contact(contact_addr),
	contentType := content_type,
	cSeq := {
		fieldName := CSEQ_E,
		seqNumber := seq_nr,
		method := method
	},
	fromField := {
		fieldName := FROM_E,
		addressField := from_addr.addr,
		fromParams := from_addr.params
	},
	toField := {
		fieldName := TO_E,
		addressField := to_addr.addr,
		toParams := to_addr.params
	},
	userAgent := {
		fieldName := USER_AGENT_E,
		userAgentBody := {
			"osmo-ttcn3-hacks/0.23"
		}
	},
	via := via
}

private function tr_Contact(template SipAddr contact_addr) return template Contact
{
	if (istemplatekind(contact_addr, "omit")) {
		return omit;
	} else if (istemplatekind(contact_addr, "*")) {
		return *;
	} else if (istemplatekind(contact_addr, "?")) {
		return ?;
	}
	var template Contact ret := {
		fieldName := CONTACT_E,
		contactBody := {
			contactAddresses := {
				{
					addressField := contact_addr.addr,
					contactParams := contact_addr.params
				}
			}
		}
	};
	return ret;
}

private function ts_Contact(template (omit) SipAddr contact_addr) return template (omit) Contact
{
	if (istemplatekind(contact_addr, "omit")) {
		return omit;
	}
	var template (omit) Contact ret := {
		fieldName := CONTACT_E,
		contactBody := {
			contactAddresses := {
				{
					addressField := contact_addr.addr,
					contactParams := contact_addr.params
				}
			}
		}
	};
	return ret;
}


function tr_AllowMethods(template Method_List allow_methods) return template Allow {
	if (istemplatekind(allow_methods, "omit")) {
		return omit;
	} else if (istemplatekind(allow_methods, "*")) {
		return *;
	} else if (istemplatekind(allow_methods, "?")) {
		return ?;
	}
	var template Allow ret := {
		fieldName := ALLOW_E,
		methods := allow_methods
	}
	return ret
}

template MessageHeader tr_SIP_msgh_std( template CallidString call_id,
					template SipAddr from_addr,
					template SipAddr to_addr,
					template SipAddr contact_addr,
					template charstring method,
					template ContentType content_type := *,
					template integer seq_nr := ?,
					template Method_List allow_methods := *
				) modifies t_SIP_msgHeader_any := {
	allow := tr_AllowMethods(allow_methods),
	callId := {
		fieldName := CALL_ID_E,
		callid := call_id
	},
	contact := tr_Contact(contact_addr),
	contentType := content_type,
	cSeq := {
		fieldName := CSEQ_E,
		seqNumber := seq_nr,
		method := method
	},
	fromField := {
		fieldName := FROM_E,
		addressField := from_addr.addr,
		fromParams := from_addr.params
	},
	toField := {
		fieldName := TO_E,
		addressField := to_addr.addr,
		toParams := to_addr.params
	},
	userAgent := *,
	via := {
		fieldName := VIA_E,
		viaBody := {
			{
				sentProtocol := { "SIP", "2.0", "UDP" },
				sentBy := tr_HostPort(from_addr.addr.nameAddr.addrSpec.hostPort),
				viaParams := *
			}
		}
	}
}


template (value) PDU_SIP_Request ts_SIP_INVITE( CallidString call_id,
						SipAddr from_addr,
						SipAddr to_addr,
						integer seq_nr,
						template (omit) charstring body
						) := {
	requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, from_addr, "INVITE", seq_nr,
				     ts_Via_from(from_addr), f_ContentTypeOrOmit(ts_CT_SDP, body)),
	messageBody := body,
	payload := omit
}
template PDU_SIP_Request tr_SIP_INVITE( template CallidString call_id,
					template SipAddr from_addr,
					template SipAddr to_addr,
					template integer seq_nr,
					template charstring body
						) := {
	requestLine := tr_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?, "INVITE", *, seq_nr),
	messageBody := body,
	payload := omit
}

template (value) PDU_SIP_Request ts_SIP_BYE( CallidString call_id,
					     SipAddr from_addr,
					     SipAddr to_addr,
					     integer seq_nr,
					     template (omit) charstring body
						) := {
	requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
				     ts_Via_from(from_addr), f_ContentTypeOrOmit(ts_CT_SDP, body)),
	messageBody := body,
	payload := omit
}

template PDU_SIP_Request tr_SIP_BYE( template CallidString call_id,
					template SipAddr from_addr,
					template SipAddr to_addr,
					template integer seq_nr,
					template charstring body
						) := {
	requestLine := tr_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", *, seq_nr),
	messageBody := body,
	payload := omit
}


template (value) PDU_SIP_Request ts_SIP_ACK( CallidString call_id,
						SipAddr from_addr,
						SipAddr to_addr,
						integer seq_nr,
						template (omit) charstring body
						) := {
	requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, from_addr, "ACK", seq_nr,
				     ts_Via_from(from_addr), f_ContentTypeOrOmit(ts_CT_SDP, body)),
	messageBody := body,
	payload := omit
}
template PDU_SIP_Request tr_SIP_ACK( template CallidString call_id,
				     template SipAddr from_addr,
				     template SipAddr to_addr,
				     template integer seq_nr,
				     template charstring body
						) := {
	requestLine := tr_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, "ACK", *, seq_nr),
	messageBody := body,
	payload := omit
}



template (value) PDU_SIP_Response ts_SIP_Response( CallidString call_id,
						   SipAddr from_addr,
						   SipAddr to_addr,
						   charstring method,
						   integer status_code,
						   integer seq_nr,
						   charstring reason,
						   Via via,
						   template (omit) charstring body := omit
							) := {
	statusLine := ts_SIP_StatusLine(status_code, reason),
	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
				     via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
	messageBody := body,
	payload := omit
}

template PDU_SIP_Response tr_SIP_Response( template CallidString call_id,
					   template SipAddr from_addr,
					   template SipAddr to_addr,
					   template SipAddr contact_addr,
					   template charstring method,
					   template integer status_code,
					   template integer seq_nr := ?,
					   template charstring reason := ?,
					   template charstring body := ?
						) := {
	statusLine := tr_SIP_StatusLine(status_code, reason),
	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact_addr, method, *, seq_nr),
	messageBody := body,
	payload := omit
}



}
