#include <arpa/inet.h>
#include "gen_classes.h"

namespace t3devlib { namespace gen {

// ISUP codec

// ISUP is going to be difficult to encode and decode for the following reasons:
// • the bit order for the transmission is reversed: the LSB is sent first.
//   This will cause some difficulties since the order of the fields in the
//   TTCN-3 types follows the order of transmission, not the order in memory
//   (note that there are lots of fields that are less than 8-bit long).
//   Consequently the codec generator cannot be used directly to
//   encode/decode the message (since it processes the order of the buffer in
//   the memory order -> MSB first)
// • numeric fields that span across multiple bytes are not always encoded in
//   the same order
//   ∘ eg. the 'Network Number Indicator' and the 'Network Identity' are both
//     encoded in DCB but the digits are not ordered in the same way:
//     ‣ the NNI 1234 is encoded as "\x21\x43"
//     ‣ the NI  1234 is encoded as "\x12\x34"
//   ∘ for some fields the byte order is not given: eg.
// • Call Identity in the Call Reference parameter
// • Diagnostics in the Cause Indicator parameter
// • Local Reference in the Connection request parameter
//     ‣ -> in which order is it assumed to be in the test suite ?
//     ‣ when the order is given, then the Most Signifiant Byte comes first
//       -> then we should assume that this is the default order
// 
// Design strategy
// 
// Knowing that ISUP messages are a concatenation of a lot of small fields
// (less than 8 bit), the coding/decoding task will be complex since it will
// be necessary to align correctly each field. Then care about the order of
// the bits.
// 
// Some consideration should be taken to ease the work
// • The two issues above should be handled separately: one part of the codec
//   will deal with the concatenation/alignment of fields and the other one
//   will deal with the order of the bits in the fields.
// • For all the alignement/concatenation requirements, we should rely on the
//   capabilities of the codec generator. It has already been validated for
//   such a task. In order to handle the fields in the same order as in the
//   transmission, it will be necessary to reverse the order of the bits in
//   the whole buffer before decoding and after encoding. After decoding and
//   before encoding, it will also be necessary to reverse the bits an all
//   the fields and sub-fields in the TTCN-3 structure. However since this
//   order differs from one field to another, it better to merge this part
//   with the next task below.
// • The second part of the codec is to order the bits in the field correctly
//   according to the properties of the field being encoded/decoded. There
//   are some general rules, but lots of fields will have to be handled on a
//   case per case basis.



// reverse the order of the bits in the buffer between the current position
// and the current end marker
void buffer_reverse_remaining_bytes (Variable* var, Buffer& buffer) throw (DecodeError)
{
	if ((buffer.GetPosition() % 8) | (buffer.GetBitsLeft() % 8))
		throw DecodeError (var, "Unaligned data");
	
	//TODO: optimise
	for (int i = buffer.GetPosition() ; i < buffer.GetEndMarker() ; i+=8)
	{
		unsigned char c = buffer.GetValueBin()[i/8];
		unsigned char r = 0;

		for (int j=8 ; j ; j--) {
			r = (r << 1) | (c & 1);
			c = c >> 1;
		}
		buffer.Replace (i, &r, 8);
	}
}

// reverse the order of the bits in every bitstring in the message
//TODO: reverse the octetstrings too
void reverse_all_bitstrings (Variable& var)
{
	switch (var.GetBaseType()) {
	case Variable::btRecord:
	case Variable::btSet: {
		RecordSet& rs = static_cast<RecordSet&> (var);
		for (int i=rs.GetSize()-1 ; i>=0 ; i--) {
			if (rs.IsPresent (i))
				reverse_all_bitstrings (*rs.GetField(i));
		}
		break;
	}
	case Variable::btRecordOf:
	case Variable::btSetOf: {
		RecordOfSetOf& rs = static_cast<RecordOfSetOf&> (var);
		for (int i=rs.GetSize()-1 ; i>=0 ; i--) {
			reverse_all_bitstrings (*rs.GetField(i));
		}
		break;
	}
	case Variable::btUnion:
		reverse_all_bitstrings (*static_cast<Union&> (var).GetField());
		break;
	
	case Variable::btBitstring:
		goto reverse_bitstring;
	
	default:
		;
	}
	return;

reverse_bitstring:
	Bitstring& bs = static_cast<Bitstring&>(var);

	unsigned char buff[16]; // should be big enough
	assert (bs.GetLength() < 16*8);
	
	const unsigned char*	src = bs.GetValueBin();
	unsigned char*		dst = buff + (bs.GetLength() / 8);
	int			k   = bs.GetLength() % 8;

	// process pack of 8 bits
	int i;
	for (i=bs.GetLength() ; i>8 ; i-=8, src++)
	{
		unsigned char c = *src;

		for (int j=8 ; j ; j--) {
			if (k == 0) {
				dst--;
				k = 8;
			}

			*dst = (*dst >> 1) | (c & 0x80);
			c = c << 1;
		}
	}

	// process the remaining bits
	{
		unsigned char c = *src;

		for ( ; i>0 ; i--) {
			if (k == 0) {
				dst--;
				k = 8;
			}

			*dst = (*dst >> 1) | (c & 0x80);
			c = c << 1;
		}
	}

	bs.SetValueBin (buff, bs.GetLength());
}

// read a bistring and return its value with all the bits reversed
int get_reverse_value (const Bitstring& bs)
{
	unsigned char buff[sizeof(uint32_t)]; // should be big enough (assume we have 32 bits for int)
	assert (bs.GetLength() < sizeof(uint32_t)*8);
	memset (buff, 0, sizeof(uint32_t));
	
	const unsigned char*	src = bs.GetValueBin();
	unsigned char*		dst = buff + (bs.GetLength() / 8);
	int			k   = bs.GetLength() % 8;

	// process pack of 8 bits
	int i;
	for (i=bs.GetLength() ; i>8 ; i-=8, src++)
	{
		unsigned char c = *src;

		for (int j=8 ; j ; j--) {
			if (k == 0) {
				dst--;
				k = 8;
			}

			*dst = (*dst >> 1) | (c & 0x80);
			c = c << 1;
		}
	}

	// process the remaining bits
	{
		unsigned char c = *src;

		for ( ; i>0 ; i--) {
			if (k == 0) {
				dst--;
				k = 8;
			}

			*dst = (*dst >> 1) | (c & 0x80);
			c = c << 1;
		}
	}

 	uint32_t result = ntohl (*reinterpret_cast<uint32_t*> (buff));
	return result >> ((sizeof(int)*8) - bs.GetLength());
}

// map used to resolve the message type in ISUP_BICC_MSG
class IsupMsgTypeMap
{
public:
	static int GetBiccField (int msg_type)
	{
		std::map<int, int>::iterator it = msInstance.mMap.find (msg_type);

		return (it != msInstance.mMap.end()) ? it->second : msInstance.mUnknown;
	}

private:
	IsupMsgTypeMap()
	{
		mMap[1] = ISUP_BICC_MSG::id_iAM_MSG;

		mUnknown = ISUP_BICC_MSG::id_uNKNOWN_MSG;
	}

	static IsupMsgTypeMap		msInstance;

	std::map<int, int>	mMap;
	int			mUnknown;
};
IsupMsgTypeMap		IsupMsgTypeMap::msInstance;

void ISUP_BICC_MSG::PreDecode (Buffer& buffer) throw (DecodeError)
{
	int position = buffer.GetPosition();

	// read ahead one byte to get the message type
	Unsigned msg_type(8);
	msg_type.Decode (buffer);
	SetHypChosenId (IsupMsgTypeMap::GetBiccField (msg_type.GetValue()));

	buffer.SetPosition(position);
}

void IAM_MSG::PreDecode (Buffer& buffer) throw (DecodeError)
{
	buffer_reverse_remaining_bytes (this, buffer);
}

void IAM_MSG::PostDecode (Buffer& buffer) throw (DecodeError)
{
	reverse_all_bitstrings (*this);
	buffer_reverse_remaining_bytes (this, buffer);
}

void IAM_MSG::PostDecode (Buffer& buffer, DecodeError& e) throw (DecodeError)
{
	reverse_all_bitstrings (*this);

	// dump the error here
	// because there is an issue w/ propagating the exceptions in t3devkit
	e.Dump (std::cerr);
	throw DecodeIgnoreMessage (this);
}

void CDN_PAR_lv::PreDecode (Buffer& buffer) throw (DecodeError)
{
	int position = buffer.GetPosition();
	Bit8 len;
	len.Decode(buffer);
	SetHypLength (get_reverse_value (len) * 8); 

	buffer.SetPosition (position);
}


}} // namespace t3devlib::gen

