/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

    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/>.

*/



#ifndef GSML3MESSAGE_H
#define GSML3MESSAGE_H

#include "GSMCommon.h"
#include "GSMTransfer.h"

namespace GSM {


/**@name L3 Processing Errors */
//@{

class L3ReadError : public GSMError {
	public:
	L3ReadError():GSMError() {}
};
#define L3_READ_ERROR {throw L3ReadError();}

class L3WriteError : public GSMError {
	public:
	L3WriteError():GSMError() {}
};
#define L3_WRITE_ERROR {throw L3WriteError();}

//@}



/**
	This is virtual base class for the messages of GSM's L3 signalling layer.
	It defines almost nothing, but is the origination of other classes.
*/
class L3Message {

	public: 

	virtual ~L3Message() {}

	/** Return the expected message body length in bytes, not including L3 header. */
	virtual size_t bodyLength() const = 0;
	
	/** Return the expected message length in bytes, including L3 header.  */
	size_t length() const { return bodyLength()+2; }

	/** Return number of BITS needed to hold message and header.  */
	size_t bitsNeeded() const { return 8*length(); }

	/**
	  The parse() method reads and decodes L3 message bits.
	  This method invokes parseBody, assuming that the L3 header
	  has already been read.
	*/
	virtual void parse(const L3Frame& source);

	/**
		Write message PD, MTI and data bits into a BitVector buffer.
		This method invokes writeBody.
		This method is overridden in the CC protocol.
	*/
	virtual void write(L3Frame& dest) const;

	/**
		Generate an L3Frame for this message.
		The caller is responsible for deleting the memory.
	*/
	L3Frame* frame(GSM::Primitive prim=DATA) const;

	/** Return the L3 protocol discriptor. */
	virtual GSM::L3PD PD() const =0;

	/** Return the messag type indicator (MTI). */
	virtual int MTI() const =0;


	protected:

	/**
		Write the L3 message body, a method defined in some subclasses.
		If not defined, this will assert at runtime.
	*/
	virtual void writeBody(L3Frame& dest, size_t &writePosition) const;

	/**
		The parseBody() method starts processing at the first byte following the
		message type octet in the L3 message, which the caller indicates with the
		readPosition argument.
		If not defined, this will assert at runtime.
	*/
	virtual void parseBody(const L3Frame& source, size_t &readPosition);


	public:

	/** Generate a human-readable representation of a message. */
	virtual void text(std::ostream& os) const;

};




/**@name Utility functions for message parsers. */
//@{
/**
	Skip an unused LV element while parsing.
	@return number of bits skipped.
*/
size_t skipLV(const L3Frame& source, size_t &readPosition);

/**
	Skip an unused TLV element while parsing.
	@return number of bits skipped.
*/
size_t skipTLV(unsigned IEI, const L3Frame& source, size_t &readPosition);

/**
	Skip an unused TV element while parsing.
	@return number of bits skipped.
*/
size_t skipTV(unsigned IEI, size_t numBits, const L3Frame& source, size_t &readPosition);
//@}




/**
	Parse a complete L3 message into its object type.
	Caller is responsible for deleting allocated memory.
	@param source The L3 bits.
	@return A pointer to a new message or NULL on failure.
*/
L3Message* parseL3(const L3Frame& source);


std::ostream& operator<<(std::ostream& os, const GSM::L3Message& msg);



/**
	Abstract class used for GSM L3 information elements.
	See GSM 04.07 11.2.1.1.4 for a description of TLV element formatting.
	To quote the spec, four categories of standard information elements are defined: 
		- information elements of format V or TV with value part consisting of 1/2 octet (type 1); 
		- information elements of format T with value part consisting of 0 octets (type 2); 
		- information elements of format V or TV with value part that has fixed length of at least one octet (type 3); 
		- information elements of format TLV or LV with value part consisting of zero, one or more octets (type 4); 

*/
class L3ProtocolElement {

	public:

	virtual ~L3ProtocolElement() {}


	/**
	  Return the length of the value part of the element in bytes.
	  This is the core length method, referenced by all other length methods.
	  Return zero for 1/2 octet fields (type 1 elements).
	*/
	virtual size_t lengthV() const =0;

	size_t lengthTV() const { return lengthV() + 1; }

	size_t lengthLV() const { return lengthV() + 1; }

	size_t lengthTLV() const { return lengthLV() + 1; }


	/**
	  The parseV method decodes L3 message bits from fixed-length value parts.
	  This is the core parse method for fixed-length parsable elements and
	  all other parse methods use it.
	  @param src The L3Frame to be parsed.
	  @param rp Bit index of read position (updated by read).
	*/
	virtual void parseV(const L3Frame& src, size_t &rp ) =0;

	/**
	  The parseV method decodes L3 message bits from variable-length value parts.
	  This is the core parse method for variable-length parsable elements and
	  all other parse methods use it.
	  @param src The L3Frame to be parsed.
	  @param rp Bit index of read position (updated by read).
	  @param expectedLength Length of available field, in bytes.
	*/
	virtual void parseV(const L3Frame& src, size_t &rp, size_t expectedLength) =0;


	/**
	  Parse LV format.
	  @param src The L3Frame to be parsed.
	  @param rp Bit index of read position (updated by read).
	*/
	void parseLV(const L3Frame& src, size_t &rp);

	/**
	  Parse TV format.
	  @param IEI The expected T part value.
	  @param src The L3Frame to be parsed.
	  @param rp Bit index of read position (updated by read).
	  @return true if the IEI matched and the element was actually read.
	*/
	bool parseTV(unsigned IEI, const L3Frame& src, size_t &rp);

	/**
	  Parse TLV format.
	  @param IEI The expected T part value.
	  @param src The L3Frame to be parsed.
	  @param rp read index (updated by read).
	  @return true if the IEI matched and the element was actually read.
	*/
	bool parseTLV(unsigned IEI, const L3Frame& src, size_t &rp);

	/**
		Write the V format.
		This is the core write method for writable elements and
		all other write methods use it.
		@param dest The target L3Frame.
		@param wp The write index (updated by write).
	*/
	virtual void writeV(L3Frame& dest, size_t &wp) const =0;

	/**
		Write LV format.
		@param dest The target L3Frame.
		@param wp The write index (updated by write).
	*/
	void writeLV(L3Frame& dest, size_t &wp) const;

	/**
		Write TV format.
		@param IEI The "information element identifier", ie, the T part.
		@param dest The target buffer.
		@param wp The buffer write pointer (updated by write).
	*/
	void writeTV(unsigned IEI, L3Frame& dest, size_t &wp) const;

	/**
		Write TLV format.
		@param IEI The "information element identifier", the T part.
		@param dest The target L3Frame.
		@param wp The write index (updated by write).
	*/
	void writeTLV(unsigned IEI, L3Frame& dest, size_t &wp) const;

	/** Generate a human-readable form of the element. */
	virtual void text(std::ostream& os) const
		{ os << "(no text())"; }


	protected:

	/**
		Skip over all unsupported extended octets in elements that use extension bits.
		@param src The L3Frame to skip along.
		@param rp The read pointer.
	*/
	void skipExtendedOctets( const L3Frame& src, size_t &rp );


};


std::ostream& operator<<(std::ostream& os, const L3ProtocolElement& elem);


}; // GSM


#endif

// vim: ts=4 sw=4
