#include "hamming.h"


#define NUM_CHECK_BITS 7 // plus parity bit
#define BIT_MASK       0x1
#define SIZE           8 // 8 words 64 bits

//#define printf_debugging

//corrected matrix provided by Cesar

unsigned int pbits0[NUM_CHECK_BITS][SIZE] ={{0x5B, 0xAD, 0xAA, 0x56, 0x55, 0x55, 0x55, 0xAB},       // check vector 1
                                            {0x6D, 0x36, 0x33, 0x9B, 0x99, 0x99, 0x99, 0xCD},       // check vector 2
                                            {0x8E, 0xC7, 0xC3, 0xE3, 0xE1, 0xE1, 0xE1, 0xF1},       // check vector 3
                                            {0xF0, 0x07, 0xFC, 0x03, 0xFE, 0x01, 0xFE, 0x01},       // check vector 4
                                            {0x00, 0xF8, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0x01},       // check vector 5
                                            {0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0x01},       // check vector 6
                                            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}        // check vector 7
};
/*

input :
	frame - buffer with entire msg
	ichunk - index of the first byte in the chunk which is being processed
	chunks - index of the parity byte (in original Cesar's, it was the number of all chunks, since all the parity bytes where at the end)
	
	nBytep - output - the number of the byte (in the chunk) in which the bitflip is
	nBitp  - output - the number of the bit (in the nByte) in which the bitflip is
return: 
	0 = no errors
	1 = single error, so corrected
	2 = double error, could not correct

*/
unsigned int hamming_decode(std::string frame,unsigned int ichunk, unsigned int chunks, unsigned int *nBytep, unsigned int *nBitP)
{

	unsigned char parity[]="";
	unsigned int  checkBitResult=0;
	unsigned result;

	//calculate parity of the chunk (8 byte)
	for(int i=0; i<NUM_CHECK_BITS; ++i) 
	{	// parity vector x
		int temp=0;
		for(int j=0 ; j<SIZE ; j++)		    // byte by byte the frame
		{
		  temp =  ((bitset<8>(static_cast<unsigned char>(frame[ichunk + j])) & bitset<8>(pbits0[i][j])).count());
		  parity[0] ^= (unsigned char)((((bitset<8>((uint8_t)(frame[ichunk + j])) & bitset<8>(pbits0[i][j])).count() & 0x1 ? 1 : 0)<< (i+1)));
		}
	}

	// Parity bit, XOR of all the bits and the party bits
	unsigned numBits=0;
	for(int j=0 ; j<SIZE ; j++)		 // byte by byte the frame and the parity vector
	{
		numBits += (bitset<8>(static_cast<unsigned char>(frame[ichunk+j])).count());
	}
	parity[0] ^= (unsigned char)(numBits & 0x1 ? 1 : 0); // Postion 0 is for the parity bit */

	//check parity bit (for all bits in the chunk)
	unsigned int parityBit = 0;
	if((parity[0] & 0x1) == (static_cast<unsigned char>(frame[chunks]) & 0x1))
		parityBit = 1;		// No error or double error

	#ifdef printf_debugging
	int d;
	for(d=0; d<8;d++)
	    printf(" 0x%x\n",static_cast<unsigned char>(frame[d+ichunk]));
	printf("parity: 0x%x vs 0x%x\n",static_cast<unsigned char>(frame[d+ichunk]),parity[0]);
	#endif
		
		
	checkBitResult = 0;
	for(int i=1;i<NUM_CHECK_BITS;i++)
		checkBitResult ^= (((parity[0] >> (i)) ^ (static_cast<unsigned char >(frame[chunks]) >> (i))) & (BIT_MASK))<< (i-1);

	if((checkBitResult!=0) && (parityBit == 1 ))
	{
		result = 2; 	// Double Error;
	}
	else if(checkBitResult != 0 )// single error, we can repair it
	{
		checkBitResult = postionInfraeme(checkBitResult);
		unsigned int nByte = checkBitResult / SIZE;
		unsigned int nBit = checkBitResult % SIZE;
		*nBitP = nBit;
		*nBytep = nByte;
		result = 1 ; 	//repaired
	}
	else // No errors
	{
		result = 0;
	}

	return result ;
}

int postionInfraeme(int postion)
{
	int postionDiff;

	if((postion >4) && (postion <=7)) postionDiff = postion - 3;
	else if((postion >8)&& (postion <=15)) postionDiff = postion - 4;
	else if((postion >16)&& (postion <=31)) postionDiff = postion - 5;
	else if(postion >32) postionDiff = postion - 6;

	// the error is in the parity checks


	return postionDiff;
}

std::string hamming_encode(const  char *frame)
{
	unsigned char parity[]="";
	for(int i=0; i<NUM_CHECK_BITS; ++i) 
	{// parity vector x
		for(int j=0 ; j<SIZE ; j++)		 // byte by byte the frame and the parity vector
		{
		   parity[0] ^= (unsigned char)((((bitset<8>(frame[j]) & bitset<8>(pbits0[i][j])).count() & 0x1 ? 1 : 0) << (i+1)));
		}
	}
	// Parity bit, XOR of all the bits and the party bits
	int numBits=0;
	for(int j=0 ; j<SIZE ; j++)		 // byte by byte the frame and the parity vector
	{
		numBits += (bitset<8>(frame[j]).count());
	}
	parity[0] ^= (unsigned char)(numBits & 0x1 ? 1 : 0); // Postion 0 is for the parity bit */
	string eFrame;
	eFrame.resize(1);

	if(parity[0]== 0)     // if the parity check is 0
		eFrame[0] = 0; // set to 0000000
	else
		eFrame[0]= parity[0]; // in the last byte parity word

	return eFrame;
}


