#include <iostream>
#include <string.h>
using namespace std;


//***************************************************************************
//
// Implementation of DecompressJCALG1 in C++ by Jeffrey Lim
// 
// JCALG1 is copyright (C) Jeremy Collake
//
// Note: This code should work on any little-endian system that supports
//		 mis-aligned reads. To support non-little endian, the functions
//		 CompressionSourceData::GetBit() and CompressionSourceData::GetBits(int)
//		 need to be modified.
//		 If the target system does not support mis-aligned reads, either
//		 the actual decompresssion data has to be made to align to 4-byte 
//		 boundaries [note that there's a 10-byte header on JCALG1], or else 
//		 the CompressionSourceData routines need to be modified.
//
//***************************************************************************

//***************************************************************************

class CompressionSourceData
{
private:
	const unsigned *Data;
	unsigned		BitBuffer;
	int				BitsRemaining;

public:
	CompressionSourceData(const unsigned *iData);

	int GetBit();
	int GetBits(int Count);
	int GetInteger();
};

CompressionSourceData::CompressionSourceData(const unsigned *iData)
{
	Data			= iData;
	BitBuffer		= 0;
	BitsRemaining	= 0;
}

int CompressionSourceData::GetBit()
{
	int ReturnValue;

	if(!BitsRemaining)
	{
		BitsRemaining = 32;
		BitBuffer = *Data++;
	}

	ReturnValue = BitBuffer >> 31;
	BitBuffer <<= 1;
	BitsRemaining--;
printf("dbr %d, ", BitBuffer);
	return ReturnValue;
}

int CompressionSourceData::GetBits(int Count)
{
	if(BitsRemaining >= Count)
	{
		int ReturnValue = BitBuffer >> (32-Count);
		BitBuffer <<= Count;
		BitsRemaining -= Count;
		return ReturnValue;
	}
	else
	{
		int Remainder = Count-BitsRemaining;

		int ReturnValue = BitBuffer >> (32-BitsRemaining) << Remainder;
		BitBuffer = *Data++;

		ReturnValue |= BitBuffer >> (32-Remainder);
		BitsRemaining = 32-Remainder;
		BitBuffer <<= Remainder;

		return ReturnValue;
	}
}

int CompressionSourceData::GetInteger()
{
	int Value = 1;
	do
	{
		Value = (Value<<1) + GetBit();
	} while(GetBit());

	return Value;
}

//***************************************************************************

struct CompressionState
{
	int		LastIndex;
	int		IndexBase;
	int		LiteralBits;
	int		LiteralOffset;

	CompressionState() { LastIndex = 1; IndexBase = 8; }
};

//***************************************************************************

void TransferMatch(unsigned char* &Destination, int MatchOffset, int MatchLength)
{
	unsigned char* p = Destination;
	unsigned char* s = p-MatchOffset;

	do
	{
		*p++ = *s++;
	} while(--MatchLength);

	Destination = p;
}

//***************************************************************************

void DecompressJCALG1(void *pDestination, const unsigned *pSource)
{
	CompressionState		State;
	CompressionSourceData	Source(pSource+10);		// +10 skips the header of JCALG. A more robust routine would check this
	unsigned char *Destination = (unsigned char*) pDestination;

	while(1)
	{
		if(Source.GetBit())
		{
			// Is Literal
			*Destination++ = Source.GetBits(State.LiteralBits) + State.LiteralOffset;
		}
		else
		{
			// Isn't literal
			if(Source.GetBit())
			{
				// Normal phrase
				int HighIndex = Source.GetInteger();

				if(HighIndex == 2) // Use the last index
				{
					int PhraseLength = Source.GetInteger();
					TransferMatch(Destination, State.LastIndex, PhraseLength);
				}
				else
				{
					State.LastIndex = ((HighIndex-3) << State.IndexBase) + Source.GetBits(State.IndexBase);

					int PhraseLength = Source.GetInteger();

					if(State.LastIndex >= 0x10000)		PhraseLength += 3;
					else if(State.LastIndex >= 0x37FF)	PhraseLength += 2;
					else if(State.LastIndex >= 0x27F)	PhraseLength++;
					else if(State.LastIndex <= 127)		PhraseLength += 4;

					TransferMatch(Destination, State.LastIndex, PhraseLength);
				}
			}
			else
			{
				if(Source.GetBit())
				{
					// OneBytePhrase or literal size change
					int OneBytePhraseValue = Source.GetBits(4) - 1;
					if(OneBytePhraseValue == 0)
					{
						*Destination++ = 0;
					}
					else if(OneBytePhraseValue > 0)
					{
						*Destination = *(Destination-OneBytePhraseValue);
						Destination++;
					}
					else
					{
						if(Source.GetBit())
						{
							// Next block
							do
							{
								for(int i = 0; i < 256; i++) *Destination++ = Source.GetBits(8);
							} while(Source.GetBit());
						}
						else
						{
							// New literal size
							State.LiteralBits = 7+Source.GetBit();
							State.LiteralOffset = 0;
							if(State.LiteralBits != 8)
							{
								State.LiteralOffset = Source.GetBits(8);
							}
						}
					}
				}
				else
				{
					// Short match
					int NewIndex = Source.GetBits(7);
					int MatchLength = 2+Source.GetBits(2);
printf("ni %d, ml %d ", NewIndex, MatchLength);
					if(NewIndex == 0)
					{
						// Extended short
						if(MatchLength == 2) break;	// End of decompression

						State.IndexBase = Source.GetBits(MatchLength+1);
					}
					else
					{
						State.LastIndex = NewIndex;
						TransferMatch(Destination, State.LastIndex, MatchLength);
					}
				}
			}
		}
	}
}

//***************************************************************************


int main() {
	// your code goes here
    unsigned int* s = new unsigned int[22]{
74, 67, 44, 0, 0, 0, 14, 0, 0, 0, 69, 17, 0, 32, 18, 137, 20, 250, 0, 0, 0, 32 };
    unsigned char d[44];
    DecompressJCALG1(&d, s);
    for (int i = 0; i < 44; i++) {
        printf("%d, ", d[i]);
    }
cout << endl;

	return 0;
}
