// MyMonModulePocsag.cpp: Implementierung der Klasse MonitorModulePocsag.
//
//////////////////////////////////////////////////////////////////////


#include <iostream>
using namespace std;
#ifdef WIN32
	#include <vector>
	#include <algorithm>
	#include <functional>
#endif

#include "time.h"
#include "MonitorModulePocsag.h"
#include "convert.h"
#include "MonitorLogging.h"

#ifdef _DEBUG
#undef THIS_FILE
//static char THIS_FILE[]=__FILE__;
#endif

#define LONG unsigned long

/*
 * the code used by POCSAG is a (n=31,k=21) BCH Code with dmin=5,
 * thus it could correct two bit errors in a 31-Bit codeword.
 * It is a systematic code.
 * The generator polynomial is:
 *   g(x) = x^10+x^9+x^8+x^6+x^5+x^3+1
 * The parity check polynomial is:
 *   h(x) = x^21+x^20+x^18+x^16+x^14+x^13+x^12+x^11+x^8+x^5+x^3+1
 * g(x) * h(x) = x^n+1
 */
#define BCH_POLY 03551 /* octal */
#define BCH_N    31
#define BCH_K    21

/*
 * some codewords with special POCSAG meaning
 */
#define POCSAG_SYNC     0x7CD215D8
#define POCSAG_SYNCINFO 0x7CF21436
#define POCSAG_IDLE     0x7a89c197

#define POCSAG_SYNC_WORDS ((2000000 >> 3) << 13)



//////////////////////////////////////////////////////////////////////
// Konstruktion/Destruktion
//////////////////////////////////////////////////////////////////////

MonitorModulePocsag::MonitorModulePocsag()
{
	maxVal=0.0 ;
	dcd_shreg=0 ;
	sphase=0 ;
	subsamp=0 ;
	lastbit=0 ;
	m_fTrigger=0.2 ;
	global_rx_data=0;
	// ctrl=0;
	m_bPreambel_detected=false ;
	m_bErrorCorrection=true ;
	m_iPreambelLen=0 ;
	m_bRXmode=false ;
	m_iRXErrorCount=0 ;
	MAX_RX_ERRORS=1500 ;
	PREAMBEL_MINLEN=300 ;
	m_iAlgorithmus=0 ;

	SetFilter("^1(.*)",1) ;
	for (int i=0; i<2;i++)
	{
		rx_buff[i].rx_sync=0 ;
		rx_buff[i].rx_word=0 ;
		rx_buff[i].rx_bit=0 ;
		rx_buff[i].numnibbles=0;
		rx_buff[i].func=0;
		rx_buff[i].adr=0;
		memset(rx_buff[i].buffer,0,sizeof(rx_buff[i].buffer));
		rx_buff[i].receiving=0;
	}
}

MonitorModulePocsag::~MonitorModulePocsag()
{

}

void MonitorModulePocsag::demod(float *buffer, int length)
{
	switch (m_iAlgorithmus)
	{
	case 1:
		demod_se(buffer, length) ; // war demod_se ...
		break ;
	case 0:
	default:
		demod_mg(buffer, length) ;
	} ;
}

void MonitorModulePocsag::demod_se(float *buffer, int length)
{

}

void MonitorModulePocsag::demod_mg(float *buffer, int length)
{

}

void MonitorModulePocsag::rxbit(int bit)
{
	global_rx_data <<= 1;
	global_rx_data |= ( bit==0 ? 0 : 1) ;
	//
	// Wozu dient das nachfolgende ? Alle Bits invertieren ?
	// Skyper Modus ???????
	do_one_bit(rx_buff, ~(global_rx_data));

	// Mit dieser Zeile alleine geht's zumindest bei mir ;-)
	//
	do_one_bit(rx_buff + 1, global_rx_data);
}

void MonitorModulePocsag::do_one_bit(struct rx *rx, unsigned long rx_data)
{
	unsigned char rxword;

	unsigned char dumpString[5] ;
	unsigned long data;
	unsigned char *bp;

	dumpString[0]=0x00 ;
	dumpString[1]=0x00 ;
	dumpString[2]=0x00 ;
	dumpString[3]=0x00 ;
	dumpString[4]=0x00 ;

	/* TODO: Hack fuer 64Bit Systeme. Da ist ein unsigned long 8 Byte gross. Die
	 * SYNC-Worte etc. sind aber nur 4 Byte gross. Da uns immer nur die letzten 4 Bytes
	 * interessieren werden vorderen 4 Bytes per Und-Verknüpfung auf Null gesetzt.
	 * Dann passen auch wieder die Vergleiche bei den Sync-Worten
	 *
	 * Ggf. muesste man das hier noch netter gestalten. Aber so geht's auf jeden Fall :)
	 * SE, 15.08.09
	 */
	rx_data &= 0xffffffff ;

	if ( ( (!rx->rx_sync)) ) // || (m_iRXErrorCount>MAX_RX_ERRORS) )
	{
		rx->rx_sync=0 ;
	} ;


	if (!rx->rx_sync) {
		if (isSync(rx_data)) {
			rx->rx_sync = 2 ; // war: 10 -- vielleicht auch 4/5/6 ?
			rx->rx_bit = rx->rx_word = 0;
			rx->func = -1;
			LOG_DEBUG("Sync gefunden")
			//TRACE0("Sync gefunden\n") ;
			m_iPreambelLen =0 ;
			m_bRXmode=true ;
			m_iRXErrorCount=0 ;
			return;
		}
		return;
	}

	if ((++(rx->rx_bit)) < 32) return;

	/*	one complete word received	*/
	rx->rx_bit = 0;

	unsigned long compareData =rx_data ;

	/*	check codeword	*/
	if (error_correction(rx_data)==false) {
		//TRACE2("CODEWORD ung?ltig (DATA=0x%4x = \"%s\")\n",rx_data,dumpString) ;
		/* codeword not valid	*/
		rx->rx_sync--;
		rx->rx_word++;	/*	Zeile als Programmkorrektur eingef?gt	*/
		m_iRXErrorCount++ ;
		/*Bad codeword	*/
		if (!(rx->func & (~3))) {
		/* message garbled	*/
			printmessage(rx) ;
			rx->buffer[0]= 0x00 ;
			rx->buffer[1]= 0x00 ;
		}
		rx->func = -1; /* invalidate message */
		return;
	}

	// Hier kommen wir nur hin, wenn das Codewort g?ltig ist
	//

	if (rx_data!=compareData)
	{
			//TRACE0("Error correction applied\n") ;
	};

	/* do something with the data */
	// printf("%s%s: Codeword: %08lx\n", s->dem_par->name, add_name, rx_data);
	rxword = rx->rx_word++;

	if (rxword >= 16) {
		/*	received word shoud be a frame synch	*/
		rx->rx_word = 0;
		if ((rx_data == POCSAG_SYNC) || (rx_data == POCSAG_SYNCINFO))
		{
			rx->rx_sync = 10;
			//TRACE0("erneutes Sync gefunden\n") ;
			// m_iPreambelLen =0 ;
			m_bRXmode=true ;
			m_iRXErrorCount=0 ;
		}
		else
			rx->rx_sync -= 2;
		return;
	}

	if (rx_data == POCSAG_IDLE) {
		// RotateString(dumpString, rx_data) ;

		//TRACE1("CODEWORD g?ltig (IDLE-WORD = %s) \n",dumpString) ;
		/*	it seems that we can output the message right here	*/
		if (!(rx->func & (~3)))
		{
			printmessage(rx);
			rx->buffer[0]= 0x00 ;
			rx->buffer[1]= 0x00 ;
		}
		rx->func = -1; /* invalidate message */
		return;
	}


	if ((rx_data & 0x80000000) ==0)
	{
		LONG testadr= ((rx_data >> 10) & 0x1ffff8) | ((rxword >> 1) & 7);

		if ((unsigned char)(testadr & 0x7) != (rx->rx_word>>1))
		{
			//TRACE0("Adresswort im falschen Frame -> Als Daten auswerten \n") ;
			//	rx_data |= 0x80000000 ;
		} ;
	} ;

	if (rx_data & 0x80000000 || rx_data == 0x7cd215d8) {
		//RotateString(dumpString, rx_data) ;
		// TRACE2("CODEWORD g?ltig (DATA=0x%4x = \"%s\") \n",rx_data,dumpString) ;
		/*	this is a data word	*/

		if (rx_data & 0x80000000) rx->receiving = 1;
		else rx->receiving = 0;

		if (rx->func & (~3)) {
			/* no message being received
			 * Lonesome data codeword	*/
			return;
		}
		if (rx->numnibbles > sizeof(rx->buffer) * 2 - 5) {
			/*	Warning: Message too long	*/
			printmessage(rx);
			rx->func = -1;
			rx->buffer[0]= 0x00 ;
			rx->buffer[1]= 0x00 ;
			return;
		}

		bp = rx->buffer + (rx->numnibbles >> 1);
		data = rx_data >> 11;
		if (rx->numnibbles & 1) {
			bp[0] = (bp[0] & 0xf0) | ((data >> 16) & 0xf);
			bp[1] = data >> 8;
			bp[2] = data;
		}
		else {
			bp[0] = data >> 12;
			bp[1] = data >> 4;
			bp[2] = data << 4;
		}
		rx->numnibbles += 5;
		return;
	}

	/*	process address codeword	*/
/*	if (rx_data >= POCSAG_SYNC_WORDS) {
		unsigned char func = (rx_data >> 11) & 3;
		unsigned long adr = ((rx_data >> 10) & 0x1ffff8) |
			((rxword >> 1) & 7);

		verbprintf(0, "%s%s: Nonstandard address codeword: %08lx "
			   "func %1u adr %08lx\n", s->dem_par->name, add_name, rx_data,
			   func, adr);
		return;
	}
*/

	if (!(rx->func & (~3)))
	{
		printmessage(rx);
		rx->buffer[0]= 0x00 ;
		rx->buffer[1]= 0x00 ;
	}

	/*rx->buffer[0]=0x00 ;
		rx->buffer[1]=0x00 ;
		*/


	//long tempAdr ; - Kann man da noch etwas retten ??? (Test,SE)
	rx->func = (rx_data >> 11) & 3;
	rx->adr = ((rx_data >> 10) & 0x1ffff8) | ((rxword >> 1) & 7);
	//tempAdr = ((rx_data >> 10) & 0x1ffff8) | ((rxword >> 1) & 7);
	rx->numnibbles = 0;
	/*
	if (tempAdr>8)
	{
		rx->adr=tempAdr ;

		bp=(unsigned char*) dumpString ;
		data=rx_data>> 11 ;

		if (rx->numnibbles & 1) {
			bp[0] = (bp[0] & 0xf0) | ((data >> 16) & 0xf);
			bp[1] = data >> 8;
			bp[2] = data;
		}
		else {
			bp[0] = data >> 12;
			bp[1] = data >> 4;
			bp[2] = data << 4;
		}

		//TRACE3("CODEWORD g?ltig (ADDRESS=%d ,Frame=%d, rxCount=%d) \n",rx->adr,rx->adr & 0x7,rx->rx_word>>1) ;
		if ((rx->adr & 0x7) != (rx->rx_word>>1))
			int doof=0 ;
			//TRACE0("Vermutlich ein Fehlerhaftes Adresswort !-------------------------------\n") ;
	} ;
	*/

}

void MonitorModulePocsag::printmessage(struct MonitorModulePocsag::rx *rx)
{
	char Funktionsbit [5]= " " ;

	if (rx->adr==0) return ; // Adresse == 0 macht keine Sinn ...

	Funktionsbit[0] = rx->func < 10 ? '0' + rx->func : 'A' + rx->func-10 ;
	//itoa( rx->func , Funktionsbit,16) ;

	char Adresse[10] ;
	sprintf(Adresse, "%07d", (int) rx->adr ) ;

	std::string outString ;
	std::string message="" ;

	// Fuer Crusader und FMS32Pro Modus
	char dateStr[9];
	char timeStr[9];
	//char subString[2];
	std::string subString ;

	std::string counterCString ;

	if (!isnumeric(rx, message))
	{
		RotateString (message,rx) ;
	} ;

	std::string jetzt ;

	currentTime(jetzt) ; // aktuelle Uhrzeit holen
	struct tm* tm_time= localtime(&m_time) ;
	strftime(dateStr,9,"%d.%m.%y" ,tm_time) ;
	strftime(timeStr,9,"%H:%M:%S" ,tm_time) ;


	ModuleResultBase *pRes =new ModuleResultBase() ;

	pRes->set("timestamp",jetzt);
	pRes->set("uhrzeit",timeStr) ;
	pRes->set("datum",dateStr) ;
	pRes->set("servernamehex",m_serverNameHex);
	pRes->set("channelnamehex",m_channelNameHex);
	pRes->set("channelnum",convertIntToString(m_iChannelNum));

	pRes->set("typ","pocsag");
	pRes->set("subhex",subString) ;
	pRes->set("sub",std::string(Funktionsbit)) ;
	pRes->set("ric",Adresse) ;
	pRes->set("text",message) ;

	pRes->Dump();

	GlobalDispatcher->addResult(pRes) ;

	// StoreResult(rx) ;
}

unsigned int MonitorModulePocsag::syndrome(unsigned long data)
{
	unsigned long shreg = data >> 1; /* throw away parity bit */
	unsigned long mask = 1L << (BCH_N-1), coeff = BCH_POLY << (BCH_K-1);
	int n = BCH_K;

	for(; n > 0; mask >>= 1, coeff >>= 1, n--)
		if (shreg & mask)
			shreg ^= coeff;
	if (even_parity(data))
		shreg |= (1 << (BCH_N - BCH_K));
//	printf("BCH syndrome: data: %08lx syn: %08lx\n", data, shreg);
	return shreg;
}

unsigned char MonitorModulePocsag::even_parity(unsigned long data)
{
	unsigned int temp = data ^ (data >> 16);

	temp = temp ^ (temp >> 8);
	temp = temp ^ (temp >> 4);
	temp = temp ^ (temp >> 2);
	temp = temp ^ (temp >> 1);
	return temp & 1;
}

bool MonitorModulePocsag::error_correction(unsigned long & rx_data)
{
	if (!syndrome(rx_data)) return true ;
	if (!m_bErrorCorrection)  return false ;

	if (rx_data==0) return false ;

	int bit1=0,bit2=0 ;
	unsigned long mask1=1 , mask2 ;

	bit2=0 ;
	mask2=1 ;

	// Im ersten Lauf alle Bit's einmal invertieren
	//
	do
	{
		rx_data ^= mask2 ;
		if (!syndrome(rx_data)) return true ;
		bit2++ ;
		rx_data ^= mask2 ;
		mask2 <<=1 ;
	} while (bit2<31) ;

	// Offensichtlich bringt das invertieren eines Bits nichts, also versuchen 2 Bits
	// zu invertieren
	//
	do
	{	// Das Bit laut Z?hler bit1 invertieren
		//
		rx_data ^= mask1 ;
		if (!syndrome(rx_data)) return true ;

		bit2=bit1 ;
		mask2= 1 ;
		mask2 <<= bit2 ;

		do
		{	// Als zweites das Bit bit2 invertieren
			//
			rx_data ^= mask2 ;
			if (!syndrome(rx_data)) return true ;
			bit2++ ;
			rx_data ^= mask2 ;
			mask2 <<=1 ;
		} while (bit2<32) ;

		bit1 ++ ;
		rx_data ^= mask1 ;
		mask1 <<=1 ;
	} while	 (bit1 < 32) ;

	return false ;
}

void MonitorModulePocsag::RotateString(std::string & buffer, struct rx *rx)
{
	unsigned long data = 0;
	int datalen = 0;
	unsigned char *bp = rx->buffer;
	int len = rx->numnibbles;


	unsigned char curchr;
	const char *tstr;
	bool ctrl=true ;

	while (len > 0) {
		while (datalen < 7 && len > 0) {
			if (len == 1) {
				data = (data << 4) | ((*bp >> 4) & 0xf);
				datalen += 4;
				len = 0;
			} else {
				data = (data << 8) | *bp++;
				datalen += 8;
				len -= 2;
			}
		}
		if (datalen < 7)
			continue;
		datalen -= 7;
		/*	7-bit-Code, ein Shift left und revers:	*/
		curchr = ((data >> datalen) & 0x7f) << 1;
		curchr = ((curchr & 0xf0) >> 4) | ((curchr & 0x0f) << 4);
		curchr = ((curchr & 0xcc) >> 2) | ((curchr & 0x33) << 2);
		curchr = ((curchr & 0xaa) >> 1) | ((curchr & 0x55) << 1);
		tstr = translate_alpha(curchr);
		if (tstr) {
			/*	Steuerzeichen ausgeben?	*/
			if (ctrl
			|| ((curchr & 0x58) == 0x58 && (curchr & 0x7) >= 3 && (curchr & 0x7) <= 5)
			|| curchr == 0x7e) {
				buffer.append(tstr) ;
			}
		}
		else {
			buffer.append (1,curchr) ;
		}
	}
}

void MonitorModulePocsag::StoreResult(struct rx *rx)
{


}

bool MonitorModulePocsag::isnumeric(rx *rx, std::string & message)
{
	// Keine Numeric-Kontrolle mehr ;-)
	return false ;

}


void MonitorModulePocsag::SetTrigger(float trigger)
{
	m_fTrigger=trigger ;
}

bool MonitorModulePocsag::isSync(unsigned long rxdata)
{
	int counter_SYNC=0, counter_SYNCINFO=0 ;
	/* ToDo: unsigned long / signed long sind auf 64Bit System (8 !) Byte gross statt 4 !
	 * Deswegen vermutlich Fehler in der Sync-Detection !
	 */
	unsigned long VergleichSYNC=POCSAG_SYNC ;
	unsigned long VergleichSYNCINFO=POCSAG_SYNCINFO ;


	if (!m_bErrorCorrection)
		return (rxdata == POCSAG_SYNC || rxdata == POCSAG_SYNCINFO ) ;

	// Aha - Fehlerkorrektur gew?nscht
	//

	for (int i=0; i < 32 ; i++)
	{
		if ( (rxdata & 0x1) == (VergleichSYNC & 0x1) )
		{
			counter_SYNC++ ;
		} ;

		if ( (rxdata & 0x1) == (VergleichSYNCINFO & 0x1) )
		{
			counter_SYNCINFO++ ;
		} ;

		VergleichSYNC >>= 1 ;
		VergleichSYNCINFO >>= 1 ;
		rxdata >>=1 ;
	}
	return ((counter_SYNC >30) || (counter_SYNCINFO>30)) ;
}
