// COPYRIGHT (C) 1990-1999.  All Rights Reserved. - PRINT.CPP
// Zinc Software Incorporated.  Pleasant Grove, Utah  USA
// May be freely copied, used and distributed.

#include <zaf.hpp>
#include <z_bmap.hpp>
#include <z_utils.hpp>
#include "print.hpp"

// For DOS programs run under platforms such as Windows 95 or NT you
// should be sure that the printer port is captured, otherwise the app
// may appear to hang or function very slowly.

PrintWindow::PrintWindow(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafWindow(name, persist), printer()
{
	// Make sure Draw() is used to draw the client region.
	SetOSDraw(false);

	// Initialize members.
	currentPage = 1;
	prevButton = DynamicPtrCast(GetObject("PrevButton"), ZafButton);
	nextButton = DynamicPtrCast(GetObject("NextButton"), ZafButton);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType PrintWindow::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	ZafDisplay *display = Display();
	ZafRegionStruct drawRegion = BeginDraw();

	if (ccode == S_REDISPLAY_REGION)
		display->SetClipRegion(event.region);

	// Draw a 3d border around the client region.
	DrawShadow(drawRegion, -2, ccode);

	// Get information about the printer in display coordinates.
	printer.SetCoordinateType(ZAF_INCH_1000);

	int paperWidth = (int)display->ConvertXValue(printer.PaperWidth(), ZAF_INCH_1000, ZAF_PIXEL);
	int paperHeight = (int)display->ConvertYValue(printer.PaperHeight(), ZAF_INCH_1000, ZAF_PIXEL);

	// Figure out the scale needed to fit the preview page in the client area.
	long scaleNumerator, scaleDenominator;
	scaleNumerator = scaleDenominator = 1;
	if ((drawRegion.Width() * 1000L) / paperWidth >
		(drawRegion.Height() * 1000L) / paperHeight)
	{
		scaleNumerator = drawRegion.Height();
		scaleDenominator = paperHeight + 2;
	}
	else
	{
		scaleNumerator = drawRegion.Width();
		scaleDenominator = paperWidth + 2;
	}

	// Scale the paper width and height to "preview" coordinates.
	paperWidth = (int)(paperWidth * scaleNumerator / scaleDenominator);
	paperHeight = (int)(paperHeight * scaleNumerator / scaleDenominator);

	// Create a region for the preview "paper."
	ZafRegionStruct paperRegion;
	paperRegion.coordinateType = ZAF_PIXEL;
	paperRegion.left = drawRegion.left + (drawRegion.Width() - paperWidth) / 2;
	paperRegion.right = paperRegion.left + paperWidth - 1;
	paperRegion.top = drawRegion.top + (drawRegion.Height() - paperHeight) / 2;
	paperRegion.bottom = paperRegion.top + paperHeight - 1;

	// Draw the area of the client region to the left and right of the
	// preview paper.
	if (paperRegion.Width() < drawRegion.Width())
	{
		ZafRegionStruct backgroundRegion = drawRegion;
		backgroundRegion.right = paperRegion.left - 1;
		DrawBackground(backgroundRegion, ccode);
		backgroundRegion.left = paperRegion.right + 1;
		backgroundRegion.right = drawRegion.right;
		DrawBackground(backgroundRegion, ccode);
	}

	// Draw the area of the client region above and below the preview paper.
	if (paperRegion.Height() < drawRegion.Height())
	{
		ZafRegionStruct backgroundRegion = drawRegion;
		backgroundRegion.bottom = paperRegion.top - 1;
		DrawBackground(backgroundRegion, ccode);
		backgroundRegion.top = paperRegion.bottom + 1;
		backgroundRegion.bottom = drawRegion.bottom;
		DrawBackground(backgroundRegion, ccode);
	}

	// Draw the preview paper (blank white page).
	display->SetForeground(ZAF_CLR_BLACK);
	display->SetBackground(ZAF_CLR_WHITE);
	display->Rectangle(paperRegion, 1, true);
	paperRegion--;

	// Get the "page" region (the region inside the margins).
	ZafCoordinate leftMargin = 0, topMargin = 0, rightMargin = 0, bottomMargin = 0;
	printer.Margins(leftMargin, topMargin, rightMargin, bottomMargin);
	ZafRegionStruct pageRegion;
	pageRegion.coordinateType = ZAF_PIXEL;
	pageRegion.left = (int)(paperRegion.left + display->ConvertXValue(leftMargin, ZAF_INCH_1000, ZAF_PIXEL) *
		scaleNumerator / scaleDenominator - 1);
	pageRegion.top = (int)(paperRegion.top + display->ConvertYValue(topMargin, ZAF_INCH_1000, ZAF_PIXEL) *
		scaleNumerator / scaleDenominator - 1);
	pageRegion.right = (int)(paperRegion.right - display->ConvertXValue(rightMargin, ZAF_INCH_1000, ZAF_PIXEL) *
		scaleNumerator / scaleDenominator + 1);
	pageRegion.bottom = (int)(paperRegion.bottom - display->ConvertYValue(bottomMargin, ZAF_INCH_1000, ZAF_PIXEL) *
		scaleNumerator / scaleDenominator + 1);

	// Outline the margin with a dotted gray rectangle.
	display->SetForeground(ZAF_CLR_LIGHTGRAY);
	display->SetLineStyle(ZAF_LINE_DOTTED);
	display->Rectangle(pageRegion, 1, false);
	pageRegion--;
	display->SetLineStyle(ZAF_LINE_SOLID);

	// Set the scale and viewport for previewing printer api calls.
	display->SetClipRegion(pageRegion);
	display->SetOrigin(pageRegion.left, pageRegion.top);
	display->SetScale((int)scaleNumerator, (int)scaleDenominator);
	display->SetCoordinateType(ZAF_INCH_1000);

	// Draw the preview.
	DrawPage(currentPage, display);

	EndDraw();

	return (ccode);
}

void PrintWindow::DrawPage(int pageNum, ZafDisplay *display)
{
	// This function draw a page to an abstract ZafDisplay.  It doesn't
	// know if it is drawing to the screen or to the printer.

	// Set coordinate type to inches.
	display->SetCoordinateType(ZAF_INCH_1000);

	if (pageNum == 1)
	{
		// Draw some lines.
		// Line widths are passed as pixels, so convert to inches.
		display->SetForeground(ZAF_CLR_BLACK);
		int scaleNumerator, scaleDenominator;
		display->Scale(&scaleNumerator, &scaleDenominator);
		display->Line(500, 1750, 4500, 1750, 125);
		display->Line(500, 1500, 4000, 1500, 62);
		display->Line(500, 1250, 3500, 1250, 31);
		display->Line(500, 1000, 3000, 1000, 15);
		display->Line(500, 750, 2500, 750, 7);
		display->Line(500, 500, 2000, 500, 3);
		display->Line(500, 250, 1500, 250, 1);
		display->Line(500, 0, 1000, 0, ZAF_HAIR_LINE);

		// Draw a 1.5 inch diameter circle.
		display->SetForeground(ZAF_CLR_RED);
		display->Ellipse(500, 2500, 2000, 4000, 0, 360);

		// Draw a pentagon;
		display->SetForeground(ZAF_CLR_BLUE);
		display->SetBackground(ZAF_CLR_LIGHTBLUE);
		ZafCoordinate polygonPoints[10];
		polygonPoints[0] = 3000;
		polygonPoints[1] = 4000;
		polygonPoints[2] = 4000;
		polygonPoints[3] = 4000;
		polygonPoints[4] = 4309;
		polygonPoints[5] = 3049;
		polygonPoints[6] = 3500;
		polygonPoints[7] = 2461;
		polygonPoints[8] = 2691;
		polygonPoints[9] = 3049;
		display->Polygon(5, polygonPoints, 1, true, true);

		// Draw a 2 inch by 4 inch filled rectangle.
		display->SetForeground(ZAF_CLR_GREEN);
		display->SetBackground(ZAF_CLR_YELLOW);
		display->Rectangle(5000, 0, 7000, 4000, 1, true);

		// Draw a ruler.
		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_LIGHTGRAY);
		display->Rectangle(0, 4500, 12000, 5500, 1, true);
		ZafCoordinate x;

		// Draw the inch marks
		for (x = 125; x < 12000; x += 125)
		{
			ZafCoordinate length;
			if (x % 1000 == 0)
				length = 375;
			else if (x % 500 == 0)
				length = 250;
			else if (x % 250 == 0)
				length = 188;
			else
				length = 125;

			display->Line(x, 4500, x, 4500 + length);
		}

		// Draw the centimeter marks.
		display->SetCoordinateType(ZAF_MILLIMETER_10);
		ZafCoordinate y = display->ConvertYValue(5500, ZAF_INCH_1000, ZAF_MILLIMETER_10);
		for (x = 10; x < 4250; x += 10)
		{
			ZafCoordinate length;
			if (x % 100 == 0)
				length = 75;
			else if (x % 50 == 0)
				length = 45;
			else
				length = 25;

			display->Line(x, y - length, x, y);
		}


		// Draw a color scale;
		display->SetCoordinateType(ZAF_MILLIMETER_10);
		for (int i = 0; i < 16; i++)
		{
			display->SetBackground((ZafLogicalColor)i);
			display->Rectangle(i * 100, 1600, (i + 1) * 100, 1700, ZAF_HAIR_LINE, true);
		}

		// Draw some text.
		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_WHITE);

		ZafLogicalFont helveticaFont = display->AddFont(ZAF_FNT_NULL, "Helvetica", 12);
		display->SetFont(helveticaFont != ZAF_FNT_NULL ? helveticaFont : ZAF_FNT_DIALOG);
		display->Text(0, 1800, ZAF_ITEXT("Sample Text"));

		// Draw a pie.
		display->SetCoordinateType(ZAF_INCH_1000);
 		display->SetBackground(ZAF_CLR_LIGHTRED);
		display->Ellipse(3500, 7250, 6000, 9750, 0, 75, 0, true);
 		display->SetBackground(ZAF_CLR_BLUE);
		display->Ellipse(3500, 7250, 6000, 9750, 75, 135, 0, true);
 		display->SetBackground(ZAF_CLR_YELLOW);
		display->Ellipse(3500, 7250, 6000, 9750, 135, 165, 0, true);
 		display->SetBackground(ZAF_CLR_LIGHTGREEN);
		display->Ellipse(3500, 7250, 6000, 9750, 165, 200, 0, true);
 		display->SetBackground(ZAF_CLR_RED);
		display->Ellipse(3500, 7250, 6000, 9750, 200, 275, 0, true);
 		display->SetBackground(ZAF_CLR_BROWN);
		display->Ellipse(3500, 7250, 6000, 9750, 275, 310, 0, true);
 		display->SetBackground(ZAF_CLR_DARKGRAY);
		display->Ellipse(3500, 7250, 6000, 9750, 310, 360, 0, true);
		display->Ellipse(3500, 7250, 6000, 9750, 0, 360, 1, false);
	}
	else if (pageNum == 2)
	{
		// Get the dimensions of the printer page in inches.
		// (The image is drawn 1/16 inch from the right and bottom
		// margins to prevent rounding from clipping off the last pixel.)
		ZafRegionStruct drawRegion;
		drawRegion.coordinateType = ZAF_INCH_1000;
		drawRegion.left = drawRegion.top = 0;
		drawRegion.right = (int)printer.PageWidth() - 62;
		drawRegion.bottom = (int)printer.PageHeight() - 62;

		// Initialize values.
		int sunRadius = ZafMin(drawRegion.Width() / 5, drawRegion.Height() / 5);
		int wCenter = drawRegion.Width() / 2;
		int hCenter = drawRegion.Height() / 2;
		int wOneFourth = wCenter / 2;
		int hOneFourth = hCenter / 2;
		int flagTop = drawRegion.top + hOneFourth - 750;
		ZafCoordinate mountainPoints[16];
		mountainPoints[0] = drawRegion.left; mountainPoints[1] = drawRegion.top + hCenter;
		mountainPoints[2] =	drawRegion.left + wOneFourth; mountainPoints[3] = drawRegion.top + hOneFourth;
		mountainPoints[4] =	drawRegion.left + wCenter; mountainPoints[5] = drawRegion.bottom - hOneFourth;
		mountainPoints[6] =	drawRegion.right - wOneFourth; mountainPoints[7] = drawRegion.top + hCenter;
		mountainPoints[8] =	drawRegion.right; mountainPoints[9] = drawRegion.bottom - hOneFourth;
		mountainPoints[10] = drawRegion.right; mountainPoints[11] = drawRegion.bottom - 1500;
		mountainPoints[12] = drawRegion.left; mountainPoints[13] = drawRegion.bottom - 1500;
		mountainPoints[14] = drawRegion.left; mountainPoints[15] = drawRegion.top + hCenter;

		// Draw sky background.
		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_LIGHTCYAN);
		display->Rectangle(drawRegion.left, drawRegion.top, drawRegion.right,
			drawRegion.bottom - hOneFourth, 0, true);
		display->Rectangle(drawRegion, 1, false);

		// Draw sun.
		display->SetBackground(ZAF_CLR_YELLOW);
		display->Ellipse(drawRegion.right - 2 * sunRadius - 1000, 1000,
			drawRegion.right - 1000, drawRegion.top + 2 * sunRadius + 1000,
			0, 360, 1, true);

		// Draw mountains and grass.
		display->SetBackground(ZAF_CLR_LIGHTGREEN);
		display->Rectangle(drawRegion.left, drawRegion.bottom - 1500, drawRegion.right,
			drawRegion.bottom, 1, true);
		display->SetBackground(ZAF_CLR_BROWN);
		display->Polygon(8, mountainPoints, 1, true);

		// Draw the flag.
		display->SetBackground(ZAF_CLR_LIGHTGRAY);
		display->Line(drawRegion.left + wOneFourth, flagTop, drawRegion.left + wOneFourth, flagTop + 750, 2);
		display->Rectangle(drawRegion.left + wOneFourth, flagTop,
			drawRegion.left + wOneFourth + 500, flagTop + 300, 1, true);
		ZafBitmapData flagBitmap(ZAF_ITEXT("FlagBitmap"), *zafObjectPersistence);
		display->Bitmap(drawRegion.left + wOneFourth + 125, flagTop + 25, flagBitmap);
	}
}
#endif

ZafEventType PrintWindow::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;

	switch (ccode)
	{
	case PRINT_SETUP:
		// Present the printer setup dialog and display any changes.
		if (printer.PrintSetup())
			Redisplay();
		break;

	case PRINT:
		{
		// Initialize a print job structure.
		ZafPrintJobStruct job;
		job.collate = false;
		job.copies = 1;
		job.startPage =	job.endPage = currentPage;
		job.minPage = 1;
		job.maxPage = 2;

		// Present the print dialog and fill the job structure.
		if (printer.JobSetup(job))
		{
			// Display any printer setup changes.
			Redisplay();

			// Begin a print job.
			if (printer.BeginJob(ZAF_ITEXT("Printer_Test")) != ZAF_ERROR_NONE)
			{
				zafErrorSystem->ReportError(ZAF_NULLP(ZafWindowObject),
					ZAF_ITEXT("Error"), ZAF_DIALOG_OK,
					ZAF_ITEXT("Uanble to initialize print job."));

				break;
			}

			// Collate is true only if collation must be done manually.
			if (job.collate)
			{
				// copies is greater than 1 only if multiple copies must
				// be done manually.
				for (int copy = 0; copy < job.copies; copy++)
				{
					for (int page = job.startPage; page <= job.endPage; page++)
					{
						// Begin a new page.
						printer.BeginPage();

						// Draw the page.
						DrawPage(page, &printer);

						// End the page.
						printer.EndPage();
					}
				}
			}
			else
			{
				for (int page = job.startPage; page <= job.endPage; page++)
				{
					for (int copy = 0; copy < job.copies; copy++)
					{
						// Begin a new page.
						printer.BeginPage();

						// Draw the page.
						DrawPage(page, &printer);

						// End the page.
						printer.EndPage();
					}
				}
			}
			printer.EndJob();
		}
		}
		break;

	case NEXT_PAGE:
		// Display the 2nd page (there are only two pages).
		currentPage++;
		nextButton->SetDisabled(true);
		prevButton->SetDisabled(false);
		Redisplay();
		break;

	case PREV_PAGE:
		// Display the 1st page (there are only two pages).
		currentPage--;
		prevButton->SetDisabled(true);
		nextButton->SetDisabled(false);
		Redisplay();
		break;

	case N_SIZE:
		// Redraw the entire client so that scaling can take effect.
		Redisplay();
		break;

	default:
		ccode = ZafWindow::Event(event);
	}

	return (ccode);
}
