// zlib open source license
//
// Copyright (c) 2010 to 2013 David Forsgren Piuva
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//    1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 
//    2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 
//    3. This notice may not be removed or altered from any source
//    distribution.

#include "../stdAfx.h"
#include "LanguageMacros.h"

ByteArray::ByteArray(long InitialUsedSize, MessageQueue* NewMQ) {
	long InitialAllocatedSize;
	InitialAllocatedSize = InitialUsedSize * 2;
	UnusedBits = 0;
	if (InitialAllocatedSize < InitialUsedSize) InitialAllocatedSize = InitialUsedSize;
	if (InitialAllocatedSize < 128) InitialAllocatedSize = 128;
	if (NewMQ) {
		MQ = NewMQ;
	} else {
		AllocatedBytes = 0;
		UsedBytes = 0;
		return;
	}
	TheData = new unsigned char[InitialAllocatedSize];
	if (!TheData) {
		MQ->InsertMessage(L"ByteArray::ByteArray: Could not allocate memory.");
		AllocatedBytes = 0;
		UsedBytes = 0;
	} else {
		memset(TheData,0,InitialAllocatedSize);
		AllocatedBytes = InitialAllocatedSize;
		UsedBytes = InitialUsedSize;
	}
}

ByteArray::~ByteArray(void) {
	SAFE_DELETE_ARRAY(TheData)
}

void ByteArray::ResizeAllocation(long NewAllocatedSize) {
	long I;
	unsigned char* OldData;
	OldData = TheData;
	TheData = new unsigned char[NewAllocatedSize];
	if (!TheData) {
		MQ->InsertMessage(L"ByteArray::ResizeAllocation: Could not reallocate memory.");
		TheData = OldData;
	} else {
		if (TheData != NULL && OldData != NULL && AllocatedBytes != NewAllocatedSize) {
			if (NewAllocatedSize > AllocatedBytes) {
				LoopForward(0,I,AllocatedBytes-1) {
					TheData[I] = OldData[I];
				}
				LoopForward(AllocatedBytes,I,NewAllocatedSize-1) {
					TheData[I] = 0;
				}
			} else {
				LoopForward(0,I,NewAllocatedSize-1) {
					TheData[I] = OldData[I];
				}
			}
		}
		AllocatedBytes = NewAllocatedSize;
		SAFE_DELETE_ARRAY(OldData)
	}
}

void ByteArray::Resize(long NewUsedSize) {
	UsedBytes = NewUsedSize;
	UnusedBits = 0;
	if (UsedBytes > AllocatedBytes) {
		ResizeAllocation(UsedBytes * 2);
	}
}

void ByteArray::SetByte(long Index, unsigned char NewValue) {
	if (Index < 0 || Index > UsedBytes - 1) {
		MQ->InsertMessage(L"ByteArray::SetByte: Index is out of bound.");
		return;
	} else {
		TheData[Index] = NewValue;
	}
}

unsigned char ByteArray::GetByte(long Index) {
	if (Index < 0 || Index > UsedBytes - 1) {
		MQ->InsertMessage(L"ByteArray::GetByte: Index is out of bound.");
		return 0;
	} else {
		return TheData[Index];
	}
}

void ByteArray::SetBit(long Index, bool NewValue) {
	long ByteOffset;
	long BitOffset;
	BitOffset = Index % 8;
	ByteOffset = Index / 8;
	if (ByteOffset < 0 || ByteOffset > UsedBytes - 1 || Index < 0) { MQ->InsertMessage(L"ByteArray::SetBit: Index is out of bound."); return; }
	SetBitInUINT8(&(TheData[ByteOffset]),BitOffset,NewValue);
}

bool ByteArray::GetBit(long Index) {
	long ByteOffset;
	long BitOffset;
	BitOffset = Index % 8;
	ByteOffset = (Index - BitOffset) / 8;
	if (ByteOffset < 0 || ByteOffset > UsedBytes - 1 || Index < 0) { MQ->InsertMessage(L"ByteArray::GetBit: Index is out of bound."); return false; }
	return GetBitFromUINT8(TheData[ByteOffset],BitOffset);
}

void ByteArray::SetBitInByte(long ByteOffset, long BitOffset, bool NewValue) {
	if (ByteOffset < 0 || ByteOffset > UsedBytes - 1) { MQ->InsertMessage(L"ByteArray::SetBitInByte: ByteOffset is out of bound."); return; }
	if (BitOffset < 0 || BitOffset > 7) { MQ->InsertMessage(L"ByteArray::SetBitInByte: BitOffset is out of bound."); return; }
	SetBitInUINT8(&(TheData[ByteOffset]),BitOffset,NewValue);
}

bool ByteArray::GetBitInByte(long ByteOffset, long BitOffset) {
	if (ByteOffset < 0 || ByteOffset > UsedBytes - 1) { MQ->InsertMessage(L"ByteArray::GetBitInByte: ByteOffset is out of bound."); return false; }
	if (BitOffset < 0 || BitOffset > 7) { MQ->InsertMessage(L"ByteArray::GetBitInByte: BitOffset is out of bound."); return false; }
	return GetBitFromUINT8(TheData[ByteOffset],BitOffset);
}

/*
void ByteArray::PrintContent() {
	long I;
	LoopForwardLengthFromZero(I,UsedBytes) {
		if (I % 8 == 0) {
			printf("\n");
		}
		PrintBitsInUINT8(TheData[I]);
		if (I % 8 < 7) {
			printf(" ");
		}
	}
	printf("\n\n");
	if (UnusedBits > 0) {
		printf("%i unused bits\n",UnusedBits);
	}
}
*/

void ByteArray::SmoothByte(void) {
	long I;
	LoopForwardLengthFromZero(I,UsedBytes) {
		TheData[I] = SignificantToSmoothByte[TheData[I]];
	}
}

void ByteArray::SmoothByte_Inverse(void) {
	long I;
	LoopForwardLengthFromZero(I,UsedBytes) {
		TheData[I] = SmoothToSignificantByte[TheData[I]];
	}
}

long ByteArray::GetSizeInBytes(void) {
	return UsedBytes;
}

long ByteArray::GetSizeInBits(void) {
	return UsedBytes * 8;
}

long ByteArray::GetSizeInBits_ExcludingUnusedBits(void) {
	return (UsedBytes * 8) - UnusedBits;
}

bool ByteArray::IsEqual(ByteArray* Reference){
	long ByteOffset;
	long BitOffset;
	if (Reference->GetSizeInBytes() != GetSizeInBytes()) {
		return false;
	} else {
		LoopForwardLengthFromZero(BitOffset,8) {
			LoopForwardLengthFromZero(ByteOffset,UsedBytes) {
				if (Reference->GetBitInByte(ByteOffset,BitOffset) != GetBitInByte(ByteOffset,BitOffset)) {
					return false;
				}
			}
		}
	}
	return true;
}

void ByteArray::MakeCopy(ByteArray* Dest) {
	Dest->Resize(UsedBytes);
	memcpy(Dest->TheData,TheData,UsedBytes);
}

void ByteArray::MakeCopyWithSortedBits(ByteArray* Dest) {
	long I;
	long ByteOffset;
	long BitOffset;
	Dest->Resize(UsedBytes);
	I = 0;
	LoopForwardLengthFromZero(BitOffset,8) {
		LoopForwardLengthFromZero(ByteOffset,UsedBytes) {
			Dest->SetBit(I, GetBitInByte(ByteOffset,BitOffset));
			I++;
		}
	}
}

void ByteArray::MakeCopyWithSortedBits_Inverse(ByteArray* Dest) {
	long I;
	long ByteOffset;
	long BitOffset;
	Dest->Resize(UsedBytes);
	ByteOffset = 0;
	BitOffset = 0;
	LoopForwardLengthFromZero(I,UsedBytes * 8) {
		Dest->SetBitInByte(ByteOffset,BitOffset,GetBit(I));
		ByteOffset++;
		if (ByteOffset >= UsedBytes) {
			ByteOffset = 0;
			BitOffset++;
		}
	}
}

void ByteArray::InsertByte(unsigned char NewValue) {
	if (UnusedBits != 0) {
		MQ->InsertMessage(L"ByteArray::InsertByte: You can't insert a byte with unused bits remaining. Use LeaveUnusedBits to leave padding.\n");
		return;
	}
	Resize(UsedBytes + 1);
	SetByte(UsedBytes - 1,NewValue);
}

void ByteArray::InsertBit(bool NewValue) {
	if (UnusedBits == 0) {
		Resize(UsedBytes + 1);
		UnusedBits = 7;
		if (NewValue) {
			SetByte(UsedBytes - 1,128);
		} else {
			SetByte(UsedBytes - 1,0);
		}
	} else {
		UnusedBits--;
		SetBitInByte(UsedBytes - 1,7 - UnusedBits,NewValue);
	}
}

void ByteArray::LeaveUnusedBits(void) {
	UnusedBits = 0;
}

void ByteArray::DeriveInModulo(void) {
	long I;
	int Previous;
	int NewData;
	Previous = 0;
	LoopForwardLengthFromZero(I,UsedBytes) {
		NewData = (int)TheData[I];
		TheData[I] = (unsigned char)(((384 + NewData) - Previous) % 256);
		Previous = NewData;
	}
}

void ByteArray::DeriveInModulo_Inverse(void) {
	long I;
	int Sum;
	Sum = 0;
	LoopForwardLengthFromZero(I,UsedBytes) {
		Sum = (Sum + (TheData[I] + 384)) % 256;
		TheData[I] = (unsigned char)Sum;
	}
}

UINT64 ByteArray::GetCheckSum(void) {
	UINT64 checksum;
	unsigned char Random[8];
	int I;
	int CounterA, CounterB, CounterC, CounterD, CounterE;
	checksum = 0;
	Random[0] = 17;
	Random[1] = 212;
	Random[2] = 32;
	Random[3] = 68;
	Random[4] = 11;
	Random[5] = 248;
	Random[6] = 12;
	Random[7] = 223;
	CounterA = 5;
	CounterB = 1;
	CounterC = 6;
	CounterD = 3;
	CounterE = 4;
	
	LoopForwardLengthFromZero(I,UsedBytes) {
		Random[CounterA] = (Random[CounterA] + TheData[I]) % 256;
		Random[(CounterA + 1) % 8] = (Random[(CounterA + 1) % 8] ^ TheData[I]) % 256;
		Random[(CounterA + 2) % 8] = (Random[(CounterA + 2) % 8] + TheData[I]) % 256;
		Random[(CounterA + 3) % 8] = (Random[(CounterA + 3) % 8] ^ Random[CounterC % 8]) % 256;
		Random[(CounterA + 4) % 8] = (Random[(CounterA + 4) % 8] + Random[CounterB % 8]) % 256;
		Random[(CounterA + 5) % 8] = (Random[(CounterA + 5) % 8] + Random[CounterE % 8]) % 256;
		Random[(CounterA + 6) % 8] = (Random[(CounterA + 6) % 8] ^ Random[CounterC % 8]) % 256;
		Random[(CounterA + 7) % 8] = (Random[(CounterA + 7) % 8] ^ Random[CounterE % 8]) % 256;
		
		CounterA = (CounterA + 1) % 8;
		CounterB = (CounterB + (Random[1] % 8)) % 8;
		CounterC = (CounterC + (Random[3] % 8)) % 8;
		CounterD = (CounterD + (Random[4] % 8)) % 8;
		CounterE = (CounterE + (Random[6] % 8)) % 8;
	}
	memcpy(&checksum,Random,8);
	
	return checksum;
}
