#include "gtp_codets.h"
#include "gen_classes.h"
#include <iostream>
#include <fstream>
#include <math.h>

int oct2int(std::string ov) {
// #ifdef DEBUG_LOG
	// std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	// logfile << ">>> oct2int: " << ov << std::endl;
// #endif
	std::string hexa("0123456789ABCDEF");
	int len = ov.length() - 3;
	int v = 0;
	for (int i = 1; i <= len; i += 1) {
// #ifdef DEBUG_LOG
		// logfile << "oct2int: ov[" << i << "]=" << ov[i] << std::endl;
// #endif
		if (ov[i] != 0x30) {
			v += hexa.find(ov[i]) * (int)pow(16, len - i);
// #ifdef DEBUG_LOG
			// logfile << "oct2int: v=" << v << std::endl;
// #endif
		}
	} // End of 'while' statement
	
// #ifdef DEBUG_LOG
	// logfile << "o<<< ct2int: " << v << std::endl;
	// logfile.close();
// #endif
	return v;
} // End of function oct2int

namespace t3devlib { namespace gen {

//------------------------------------------------------------
/*
type record GTPv1_MSG {
	GTPv1_Header  header,
	octetstring	  body optional
}    
}*/
//============================================================
void GTPv1_MSG::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
#ifdef DEBUG_LOG
	std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	logfile << "GTPv1_MSG::PreDecodeField: " << id << std::endl;
#endif
	switch(id) {
	case id_header: // header field
		SetHypFieldIsPresent(id_body, 1);
		break;
	case id_body: // body field
#ifdef DEBUG_LOG
		logfile << "GTPv1_MSG::PreDecodeField: len=" << Get_header().Get_len().GetValueTCIString() << std::endl;
#endif
		if (Get_header().Get_len().GetValueTCIString() == "'0000'O")
		{
			SetHypFieldIsPresent(id_body, 0); // Omitted
#ifdef DEBUG_LOG
			logfile << "GTPv1_MSG::PreDecodeField: id_body is omitted" << std::endl;
#endif
		}
		else 
		{
			int len = 8 * oct2int(Get_header().Get_len().GetValueTCIString());
			if (Get_header().IsPresent(Get_header().id_seqnum)) {
				len -= 16; // Remove two bytes
			}
			if (Get_header().IsPresent(Get_header().id_npdu_num)) {
				len -= 8; // Remove one byte
			}
			if (Get_header().IsPresent(Get_header().id_extensionheader)) {
				len -= 8; // Remove one byte
			}
			SetHypFieldLength(id_body, len); // Final length
#ifdef DEBUG_LOG
			logfile << "GTPv1_MSG::PreDecodeField: id_body length: '" << buffer.GetLength() - buffer.GetPosition() << "' - '" << len << "'" << std::endl;
#endif
		}
		break;
	default:
#ifdef DEBUG_LOG
		logfile << "GTPv1_MSG::PreDecodeField: id# " << id << " is catched by default" << std::endl;
#endif
		break;
	} // End of 'switch' statement
#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_MSG::PreDecodeField

void GTPv1_MSG::PostDecode (Buffer& buffer) throw (DecodeError)
{
#ifdef DEBUG_LOG
	std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	logfile << "GTPv1_MSG::PostDecode" << std::endl;
	logfile << "GTPv1_MSG::PreDecodeField: id_header: " << Get_header() << std::endl;
	logfile << "GTPv1_MSG::PreDecodeField: id_body: " << Get_body() << std::endl;
	DumpSource(logfile);
	logfile.close();
#endif
}

void GTPv1_MSG::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
#ifdef DEBUG_LOG
	std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	logfile << "GTPv1_MSG::PreEncodeField: " << field_id << std::endl;
#endif

	switch(field_id) {
	case id_header: // header field
		SetHypFieldIsPresent(field_id, 1);
		break;
	case id_body: // body field
#ifdef DEBUG_LOG
		logfile << "GTPv1_MSG::PreEncodeField: Header len: '" << Get_header().Get_len().GetValueTCIString() << "'" << std::endl;
#endif
		if (Get_header().Get_len().GetValueTCIString() == "'0000'O")
		{
			SetHypFieldIsPresent(id_body, 0); // Omitted
		}
		else 
		{
			SetHypFieldIsPresent(id_body, 1);
			SetHypFieldLength(id_body, 8 * oct2int(Get_header().Get_len().GetValueTCIString()));
		}
		break;
	default:
		break;
	} // End of 'switch' statement
#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_MSG::PreEncodeField

//------------------------------------------------------------
/*
type record GTPv1_Header {
    bitstring version,
    octetstring messagetype,
    octetstring len,
    octetstring teid,
    octetstring seqnum optional,
    octetstring npdu_num optional,
    octetstring extensionheader optional
}*/
//============================================================
void GTPv1_Header::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
#ifdef DEBUG_LOG
	std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	logfile << "GTPv1_Header::PreDecodeField: " << id << std::endl;
#endif

	Unsigned u5(5);
	switch(id) {
	case id_version: // Version: GTP release 99 version (1)
		SetHypFieldIsPresent(id_version, 1);
		SetHypFieldLength(id_version, 3); // Three bits length
		break;
	case id_messagetype: // Message Type field
		SetHypFieldIsPresent(id_messagetype, 1);
		SetHypFieldLength(id_messagetype, 8); // One byte length
		// Extract other flag field
		buffer.Read(u5, 5);
#ifdef DEBUG_LOG
		logfile << "GTPv1_Header::PreDecodeField: u5: '" << u5.GetValue() << "'" << std::endl;
#endif
		if (u5.GetValue() == 18) // Is Sequence Number present?
		{
#ifdef DEBUG_LOG
			logfile << "GTPv1_Header::PreDecodeField: optional fields are presents" << std::endl;
#endif
			SetHypFieldIsPresent(id_seqnum, 1);
			SetHypFieldLength(id_seqnum, 16); // Two bytes length
			SetHypFieldIsPresent(id_npdu_num, 1);
			SetHypFieldLength(id_npdu_num, 8); // One byte length
			SetHypFieldIsPresent(id_extensionheader, 1);
			SetHypFieldLength(id_extensionheader, 8); // One byte length
		} 
		else
		{
#ifdef DEBUG_LOG
			logfile << "GTPv1_Header::PreDecodeField: id# " << id_seqnum << " is omitted" << std::endl;
#endif
			SetHypFieldIsPresent(id_seqnum, 0); //  Omitted
			if (u5.GetValue() == 17) // Is N-PDU number present?
			{
				SetHypFieldIsPresent(id_npdu_num, 1);
				SetHypFieldLength(id_npdu_num, 8); // One byte length
			}
			else
			{
				SetHypFieldIsPresent(id_npdu_num, 0); // Omitted
#ifdef DEBUG_LOG
				logfile << "GTPv1_Header::PreDecodeField: id# " << id_npdu_num << " is omitted" << std::endl;
#endif
			}
			if (u5.GetValue() == 20) // Is Next Extension Header present?
			{
				SetHypFieldIsPresent(id_extensionheader, 1);
				SetHypFieldLength(id_extensionheader, 8); // One byte length
			}
			else
			{
				SetHypFieldIsPresent(id_extensionheader, 0); // Omitted
#ifdef DEBUG_LOG
				logfile << "GTPv1_Header::PreDecodeField: id# " << id_extensionheader << " is omitted" << std::endl;
#endif
			}
		}
		break;
	case id_len: // Length field
		SetHypFieldLength(id_len, 2 * 8); // Two bytes length
		break;
	case id_teid: // TEID field
		SetHypFieldLength(id_teid, 4 * 8); // Four bytes length
		break;
	default:
		break;
	} // End of 'switch' statement
#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_Header::PreDecodeField

void GTPv1_Header::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
#ifdef DEBUG_LOG
	std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	logfile << "GTPv1_Header::PreEncodeField: " << field_id << std::endl;
#endif
	
	switch(field_id) {
	case id_messagetype: {
			Unsigned u5(5); // Padding to fill flag field
			u5.SetValue(0);
			buffer.Write(u5);
		}
		break;
	case id_seqnum:
		// No break;
	case id_npdu_num:
		// No break;
	case id_extensionheader:
		SetHypFieldIsPresent(field_id, 0); // FIXME Process optional part
		break;
	default:
		break;
	} // End of 'switch' statement
#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_Header::PreEncodeField

}} // namespaces
