#include "stdafx.h"
#include "Buffer.h"

/* Implementation of class Buffer **********************************************************/

template <class CharType>
void Buffer<CharType>::Init ()
{
	BufferPartCount = 0;
	ppBufferParts = 0;
	ReaderInitialized = false;
	pCurrentWriteBufPart = 0;
	LineSeparator[0] = 0;
	LineSeparatorLen = 0;

	LineCount = 0;
	CharCount = 0;

	pLineMax = 0;
	pCharMax = 0;

	m_sDefaultOutputFilename = 0;
}

template <class CharType>
Buffer<CharType>::Buffer ()
{
	Init ();
}

template <class CharType>
Buffer<CharType>::~Buffer ()
{
	if (ppBufferParts) {
		unsigned int i;
		for (i = 0; i < BufferPartCount; i++) delete ppBufferParts[i];
		delete ppBufferParts;
	}
	if (pLineMax) delete pLineMax;
	if (pCharMax) delete pCharMax;

	if (m_sDefaultOutputFilename) delete m_sDefaultOutputFilename;
}

template <class CharType>
unsigned int Buffer<CharType>::StrLen (const CharType *s)
{
	switch (sizeof(CharType)) {
		case 1:
			return strlen ((const char *) s);
		case 2:
			return 0;
		default:
			return 0;
	}

}


template <class CharType>
void Buffer<CharType>::AddBufPart (BufPart<CharType> *pNewBufPart)
{
	BufPart<CharType> **ppOld = ppBufferParts;
	ppBufferParts = new BufPart<CharType> *[BufferPartCount + 1];
	if (ppOld) {
		unsigned int i;
		for (i = 0; i < BufferPartCount; i++) ppBufferParts[i] = ppOld[i];
		delete ppOld;
	}
	ppBufferParts[BufferPartCount] = pNewBufPart;
	pCurrentWriteBufPart = pNewBufPart;
	++BufferPartCount;
}

template <class CharType>
void Buffer<CharType>::SetLineSeparator (const CharType *sep, unsigned int seplen)
{
	if (seplen > MAX_LINESEP) seplen = MAX_LINESEP;
	memcpy (LineSeparator, sep, seplen);
	LineSeparatorLen = seplen;
}

template <class CharType>
void Buffer<CharType>::AddLine (const CharType *line)
{
	AddLine (line, (unsigned int) strlen(line));
}

template <class CharType>
void Buffer<CharType>::AddLine (const CharType *line, unsigned int len)
{
	BufPart *pNewBufPart;
	
	ReaderInitialized = false;
	
	if (!pCurrentWriteBufPart) {
		pNewBufPart = new BufPart (this);
		pNewBufPart->AddLine (line, len);
	}
	else pNewBufPart = pCurrentWriteBufPart->AddLine (line, len);

	if (pNewBufPart != 0) AddBufPart (pNewBufPart);
	LineCount++;
	CharCount += len + LineSeparatorLen;
}

template <class CharType>
const char *Buffer<CharType>::GetLine (unsigned int lineindex, unsigned int &len)
{
	if (lineindex >= LineCount) return 0;

	if (!ReaderInitialized) InitializeReader();

	if (lineindex < LastReadLine) LastReadLineIndex = 0;
	while (lineindex >= pLineMax[LastReadLineIndex]) ++LastReadLineIndex;

	LastReadLine = lineindex;
	if (LastReadLineIndex > 0) return ppBufferParts[LastReadLineIndex]->GetLine (LastReadLine - pLineMax[LastReadLineIndex-1], len);
	else return ppBufferParts[LastReadLineIndex]->GetLine (LastReadLine, len);

}

template <class CharType>
unsigned int Buffer<CharType>::GetBuffer (unsigned int offset, CharType *buffer, unsigned int buflen)
{
	if (offset >= CharCount) {
		buflen = 0;
		return 0;
	}

	if (!ReaderInitialized) InitializeReader();

	if (offset < LastReadChar) LastReadCharIndex = 0;
	while (offset >= pCharMax[LastReadCharIndex]) ++LastReadCharIndex;

	const char *srcbuf;
	unsigned int localoffset, localbuflen;
	BufPart *pBufPart;
	
	pBufPart = ppBufferParts[LastReadCharIndex];
	if (LastReadCharIndex > 0) localoffset = offset - pCharMax[LastReadCharIndex-1];
	else localoffset = offset;

	localbuflen = pBufPart->GetCharCount() - localoffset;
	if (localbuflen >= buflen || LastReadCharIndex + 1 == BufferPartCount) {
		srcbuf = pBufPart->GetBuffer (localoffset, buflen);
		memcpy (buffer, srcbuf, buflen);
		LastReadChar = offset + buflen;
		return buflen;
	}
	else {
		unsigned int restbuflen, bufoffset;

		srcbuf = pBufPart->GetBuffer (localoffset, localbuflen);
		memcpy (buffer, srcbuf, localbuflen);
		restbuflen = buflen - localbuflen;
		bufoffset = localbuflen;
		while (restbuflen > 0 && ++LastReadCharIndex < BufferPartCount) {
			pBufPart = ppBufferParts[LastReadCharIndex];
			srcbuf = pBufPart->GetBuffer (0, restbuflen);
			memcpy (buffer + bufoffset, srcbuf, restbuflen);
			bufoffset += restbuflen;
			restbuflen = buflen - bufoffset;
		}
		return bufoffset;
	}
}

#define EXPAND_LINE 50000

template <class CharType>
char *Buffer<CharType>::GetLines (unsigned int lineindex, unsigned int linecount, unsigned int *pBufLen)
{
	char *buf = new char[EXPAND_LINE];
	unsigned int charalloc = EXPAND_LINE;
	unsigned int charoffset = 0;
	const char *linebuf;
	unsigned int linelen, linemax;
	unsigned int i;

	if (lineindex + linecount >= LineCount) linemax = LineCount;
	else linemax = lineindex + linecount;

	for (i = lineindex; i < linemax; i++) {
		linebuf = GetLine (i, linelen);
		if (charoffset + linelen > charalloc) {
			if (linelen > EXPAND_LINE) charalloc += linelen;
			else charalloc += EXPAND_LINE;
			char *newbuf = new char [charalloc];
			memcpy (newbuf, buf, charoffset);
			delete buf;
			buf = newbuf;
		}
		memcpy (buf + charoffset, linebuf, linelen);
		charoffset += linelen;
	}

	if (pBufLen) *pBufLen = charoffset;
	return buf;
}


template <class CharType>
void Buffer<CharType>::GetLinesDelete (CharType *buf)
{
	delete buf;
}


template <class CharType>
void Buffer<CharType>::InitializeReader (void)
{
	unsigned int i;
	if (pLineMax) delete pLineMax;
	if (pCharMax) delete pCharMax;

	if (BufferPartCount > 0) {

		pLineMax = new unsigned int [BufferPartCount];
		pCharMax = new unsigned int [BufferPartCount];

		pLineMax[0] = ppBufferParts[0]->GetLineCount ();
		pCharMax[0] = ppBufferParts[0]->GetCharCount ();

		for (i = 1; i < BufferPartCount; i++) {
			pLineMax[i] = ppBufferParts[i]->GetLineCount() + pLineMax[i-1];
			pCharMax[i] = ppBufferParts[i]->GetCharCount() + pCharMax[i-1];
		}
	}
	LastReadLineIndex = 0;
	LastReadCharIndex = 0;
	LastReadLine = 0;
	LastReadChar = 0;
	ReaderInitialized = true;
}

template <class CharType>
void Buffer<CharType>::SetDefaultOutputFilename (const CharType *fn)
{
	if (m_sDefaultOutputFilename) delete m_sDefaultOutputFilename;
	if (!fn || !fn[0]) m_sDefaultOutputFilename = 0;
	else {
		m_sDefaultOutputFilename = new char [strlen(fn)+1];
		strcpy (m_sDefaultOutputFilename, fn);
	}
}


/* Implementation of class BufPart *********************************************************/

template <class CharType>
BufPart<CharType>::BufPart (const Buffer<CharType> *buf)
{ // Constructor
	LineCount = 0;
	CharCount = 0;
	pBuffer = buf;
}

template <class CharType>
BufPart<CharType>::~BufPart ()
{ // Destructor
}

template <class CharType>
BufPart<CharType> *BufPart<CharType>::AddLine (const CharType *line, unsigned int len)
{ // Adds a line to the object (or creates a new follow-up object to add the line)

	unsigned int lineseplen = pBuffer->GetLineSeparatorLen();
	
	if (MAX_LINE - LineCount < 1 || MAX_CHAR - CharCount < len + lineseplen) {
		BufPart *pNew = new BufPart (pBuffer);
		pNew->AddLine (line, len);
		return pNew;
	}
	else {
		LineOffset[LineCount] = CharCount;
		memcpy (Buf + CharCount, line, len);
		memcpy (Buf + CharCount + len, pBuffer->GetLineSeparator(), lineseplen);
		++LineCount;
		CharCount += len + lineseplen;
		return 0;
	}
}

template <class CharType>
const char *BufPart<CharType>::GetLine (unsigned int lineindex, unsigned int &len) const
{ // Reads the line from the object
	if (lineindex >= LineCount) return 0;
	else 
	{
		len = LineOffset[lineindex];
		return Buf + LineOffset[lineindex];
	}
}

template <class CharType>
const char *BufPart<CharType>::GetBuffer (unsigned int offset, unsigned int &len)
{ // Reads a part of the buffer from the object
	if (offset >= CharCount) return 0;
	else 
	{
		unsigned int local_len = CharCount - offset;
		if (local_len < len) len = local_len; 
		return Buf + offset;
	}
}
