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


//compiler switches:
//#define DEBUG_LOG 1
//#define DIAMETER_ONLY 1

int hex2int(const std::string &hexStr)
{
#ifdef DEBUG_LOG
	 std::ofstream logfile("C:\\Temp\\LibGtp.log", std::ios::app);
	 logfile << "hex2int string input: " << hexStr << std::endl;
#endif
	int nRes = 0;
	for (int i = 0; i < hexStr.size(); i ++)
	{
		unsigned char hex = hexStr.at(i);
		hex -= 0x30;//digits 0-9
		if(hex > 9)//Uppercase or lowercase letter
			hex -= 0x07;//'A' - '0' + 10
		if(hex > 15)//lowercase letter
			hex -= 0x20;//'a' - 'A' - '0' + 10
		nRes = (nRes << 4 ) | hex;
	} // End of 'while' statement
	
#ifdef DEBUG_LOG
	 logfile << "hex2int int result: " << nRes << std::endl;
	 logfile.close();
#endif
	return nRes;
} // End of function oct2int

namespace t3devlib { 

#ifdef DIAMETER_ONLY

void PAInit()
{
}

void SAInit()
{
}

void CDInit()
{
}

void PAReset()
{
}

void SAReset()
{
}
#endif

	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

	if(id == id_body)// body field
	{
		const std::string hexLen = Get_header().Get_len().GetValueHexa();
#ifdef DEBUG_LOG
		logfile << "GTPv1_MSG::PreDecodeField: len= " << hexLen << std::endl;
#endif
		if (!hexLen.compare("0000"))
			SetHypFieldIsPresent(id, 0); // Omitted
		else 
		{
			int len = 8 * hex2int(hexLen);
			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
			SetHypFieldIsPresent(id, 1);
			SetHypFieldLength(id, len);
#ifdef DEBUG_LOG
			logfile << "GTPv1_MSG::PreDecodeField: id_body length: '" << buffer.GetLength() - buffer.GetPosition() << "' - '" << len << "'" << std::endl;
#endif
		}
	}

#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_MSG::PreDecodeField

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

	if(field_id == id_body)// body field
	{
		const std::string hexStr = Get_header().Get_len().GetValueHexa();
#ifdef DEBUG_LOG
		logfile << "GTPv1_MSG::PreEncodeField: Header len: '" << hexStr << "'" << std::endl;
#endif
		if (!hexStr.compare("0000"))
			SetHypFieldIsPresent(field_id, 0); // Omitted
		else 
		{
			SetHypFieldIsPresent(field_id, 1);
			SetHypFieldLength(field_id, 8 * hex2int(hexStr));
		}
	} // End of 'switch' statement
#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_MSG::PreEncodeField

//------------------------------------------------------------
/*
type record GTPv1_Header {
	Bit3 version,
	Bit1 PT,
	Bit1 spare,
	Bit1 E_flag,
	Bit1 S_flag,
	Bit1 PN_flag,
	Oct1 messagetype,
	Oct2 len,
	Oct4 teid,
	Oct2 seqnum optional,
	Oct1 npdu_num optional,
	Oct1 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 char buff;
	switch(id) {
		case id_seqnum:
			buff = Get_S_flag().GetValue().at(0); 
			int nS_flag =  (buff >> 7);
			
			if(nS_flag)
			{
				SetHypFieldIsPresent(id, 1);
				SetHypFieldLength(id, 16); // 2 byte length
			}
			else
				SetHypFieldIsPresent(id, 0);
			break;
		case id_npdu_num:
			buff = Get_PN_flag().GetValue().at(0); 
			int nPN_flag =  (buff >> 7);
			
			if(nPN_flag)
			{
				SetHypFieldIsPresent(id, 1);//TODO:
				SetHypFieldLength(id, 8); // 1 byte length
			}
			else
				SetHypFieldIsPresent(id, 0);
			break;
		case id_extensionheader:
			buff = Get_E_flag().GetValue().at(0); 
			int nE_flag =  (buff >> 7);
			
			if(nE_flag)
			{
				SetHypFieldIsPresent(id, 1);//TODO:
				SetHypFieldLength(id, 8); // 1 byte length
			}
			else
				SetHypFieldIsPresent(id, 0);
			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
	unsigned char buff;
	switch(field_id) {
		case id_seqnum:
			buff = Get_S_flag().GetValue().at(0); 
			int nS_flag =  (buff >> 7);
			
			if(nS_flag)
				SetHypFieldIsPresent(field_id, 0);
			break;
		case id_npdu_num:
			buff = Get_PN_flag().GetValue().at(0); 
			int nPN_flag =  (buff >> 7);
			
			if(!nPN_flag)
				SetHypFieldIsPresent(field_id, 0);
			break;
		case id_extensionheader:
			buff = Get_E_flag().GetValue().at(0); 
			int nE_flag =  (buff >> 7);

			if(!nE_flag)
				SetHypFieldIsPresent(field_id, 0);
			break;
	default:
		break;
	} // End of 'switch' statement
#ifdef DEBUG_LOG
	logfile.close();
#endif
} // End of method GTPv1_Header::PreEncodeField

}} // namespaces
