/******************************************************************************
 JPTPrinter.cc

	This class implements the functions required to print plain text.

	BASE CLASS = JPrinter

	Copyright © 1999 by John Lindal. All rights reserved.

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

#include <JPTPrinter.h>
#include <JLatentPG.h>
#include <JString.h>
#include <jStreamUtil.h>
#include <jStrStreamUtil.h>
#include <jASCIIConstants.h>
#include <jAssert.h>

const JCharacter* kLineNumberMarginStr = "  ";
const JSize kLineNumberMarginWidth     = 2;

// setup information

const JFileVersion kCurrentSetupVersion = 1;
const JCharacter kSetupDataEndDelimiter = '\1';

	// version 1 adds itsPrintLineNumberFlag

/******************************************************************************
 Constructor

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

JPTPrinter::JPTPrinter()
	:
	JPrinter()
{
	itsCopyCount           = 1;
	itsFirstPageIndex      = 0;
	itsLastPageIndex       = 0;
	itsPageWidth           = 80;		// characters
	itsPageHeight          = 60;		// lines
	itsTabWidth            = 8;			// characters
	itsPrintLineNumberFlag = kFalse;
}

/******************************************************************************
 Destructor

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

JPTPrinter::~JPTPrinter()
{
}

/******************************************************************************
 Set page size

	The page width is measured in characters, while the page height is
	measured in lines.

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

void
JPTPrinter::SetPageWidth
	(
	const JSize width
	)
{
	assert( width > 0 );
	itsPageWidth = width;
}

void
JPTPrinter::SetPageHeight
	(
	const JSize height
	)
{
	assert( height > 0 );
	itsPageHeight = height;
}

/******************************************************************************
 Print (protected)

	Prints the given text to the output stream, formatting it into
	pages based on out page width (characters) and page height (lines).
	Returns kFalse if the process was cancelled.

	Derived classes can override the header and footer functions to
	print a header and/or footer on each page.

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

JBoolean
JPTPrinter::Print
	(
	const JCharacter*	text,
	ostream&			trueOutput
	)
{
	const JSize headerLineCount = GetHeaderLineCount();
	const JSize footerLineCount = GetFooterLineCount();
	assert( itsPageHeight > headerLineCount + footerLineCount );

	const JSize lineCountPerPage = itsPageHeight - headerLineCount - footerLineCount;

	JLatentPG pg;
	pg.VariableLengthProcessBeginning("Printing page...", kTrue, kFalse);
	JBoolean keepGoing = kTrue;

	JUnsignedOffset i   = 0;
	JIndex pageIndex    = 0;
	JSize printCount    = 0;
	JSize textLineCount = 0;
	while (keepGoing && text[i] != '\0')
		{
		pageIndex++;
		const JBoolean shouldPrintPage =
			JI2B(itsFirstPageIndex <= pageIndex &&
				 (itsLastPageIndex == 0 || pageIndex <= itsLastPageIndex));

		ostrstream bitBucket;
		ostream* output = shouldPrintPage ? (&trueOutput) : (&bitBucket);

		if (shouldPrintPage)
			{
			printCount++;
			if (printCount > 1)
				{
				*output << kJFormFeedKey << '\n';
				}
			}

		if (headerLineCount > 0)
			{
			PrintHeader(*output, pageIndex);
			}

		JSize lineNumberWidth = 0;
		if (itsPrintLineNumberFlag)
			{
			const JString lastLineIndexStr(pageIndex * lineCountPerPage, 0);
			lineNumberWidth = lastLineIndexStr.GetLength();
			}

		JSize lineCount = 0;
		while (lineCount < lineCountPerPage && text[i] != '\0')
			{
			JSize col = 0;

			if (itsPrintLineNumberFlag)
				{
				const JString lineNumberStr(textLineCount, 0);
				const JSize spaceCount = lineNumberWidth - lineNumberStr.GetLength();
				for (JIndex j=1; j<=spaceCount; j++)
					{
					*output << ' ';
					}
				lineNumberStr.Print(*output);
				*output << kLineNumberMarginStr;

				col += lineNumberWidth + kLineNumberMarginWidth;
				}

			if (col >= itsPageWidth)	// insures progress, even in ludicrous boundary case
				{
				col = itsPageWidth - 1;
				}

			while (col < itsPageWidth && text[i] != '\n' && text[i] != '\0')
				{
				if (text[i] == '\t')
					{
					const JSize spaceCount = itsTabWidth - (col % itsTabWidth);
					for (JIndex j=1; j<=spaceCount; j++)
						{
						*output << ' ';
						}
					col += spaceCount;
					}
				else if (text[i] == kJFormFeedKey)
					{
					*output << ' ';
					col++;
					}
				else
					{
					*output << text[i];
					col++;
					}
				i++;
				}

			*output << '\n';
			if (text[i] == '\n')
				{
				i++;
				}

			lineCount++;
			textLineCount++;
			}

		if (footerLineCount > 0)
			{
			while (lineCount < lineCountPerPage)
				{
				*output << '\n';
				lineCount++;
				}

			PrintFooter(*output, pageIndex);
			}

		keepGoing = pg.IncrementProgress();
		}

	pg.ProcessFinished();
	return keepGoing;
}

/******************************************************************************
 Print header and footer (virtual protected)

	Derived classes can override these functions if they want to
	print a header or a footer.  The default is to do nothing.

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

JSize
JPTPrinter::GetHeaderLineCount()
	const
{
	return 0;
}

JSize
JPTPrinter::GetFooterLineCount()
	const
{
	return 0;
}

void
JPTPrinter::PrintHeader
	(
	ostream&		output,
	const JIndex	pageIndex
	)
{
}

void
JPTPrinter::PrintFooter
	(
	ostream&		output,
	const JIndex	pageIndex
	)
{
}

/******************************************************************************
 ReadPTSetup

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

void
JPTPrinter::ReadPTSetup
	(
	istream& input
	)
{
	JFileVersion vers;
	input >> vers;

	if (vers <= kCurrentSetupVersion)
		{
		input >> itsPageWidth >> itsPageHeight;

		if (vers >= 1)
			{
			input >> itsPrintLineNumberFlag;
			}
		}
	JIgnoreUntil(input, kSetupDataEndDelimiter);
}

/******************************************************************************
 WritePTSetup

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

void
JPTPrinter::WritePTSetup
	(
	ostream& output
	)
	const
{
	output << kCurrentSetupVersion;
	output << ' ' << itsPageWidth;
	output << ' ' << itsPageHeight;
	output << ' ' << itsPrintLineNumberFlag;
	output << kSetupDataEndDelimiter;
}
