//	GRAPH.CPP (GRAPH) - Extended graphics example.
//	COPYRIGHT (C) 1990-1995.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA
// 	May be freely copied, used and distributed.

#include <ui_win.hpp>
#include "graph.hpp"


// Converts MiniCells to pixels
#define MiniConvertY(y) ((int)((y) * display->cellHeight * display->miniNumeratorY / display->miniDenominatorY))
#define MiniConvertX(x) ((int)((x) * display->cellWidth * display->miniNumeratorX / display->miniDenominatorX))

// Palettes used for drawing graphics.
UI_PALETTE graphPalette[] =
{
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_INTERLEAVE_FILL, BLACK, RED, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY },
	{ '\260',attrib( BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_INTERLEAVE_FILL, BLACK, YELLOW, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_INTERLEAVE_FILL, BLACK, GREEN, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_INTERLEAVE_FILL, BLACK, BLUE, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_INTERLEAVE_FILL, BLACK, LIGHTRED, BW_BLACK, BW_WHITE,	GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_SOLID_FILL, BLACK, LIGHTGREEN, BW_BLACK, BW_WHITE, GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_SOLID_FILL, BLACK, DARKGRAY, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_SOLID_FILL, RED, DARKGRAY, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY },
	{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
		PTN_SOLID_FILL, GREEN, DARKGRAY, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY }
};

UI_PALETTE textPalette =
{ '\260', attrib(BLACK, BLACK), attrib(MONO_DIM, MONO_BLACK),
	PTN_INTERLEAVE_FILL, BLACK, RED, BW_BLACK, BW_BLACK, GS_GRAY, GS_GRAY
};

LINE_CHART::LINE_CHART(int left, int top, int width, int height, WOF_FLAGS woFlags)
	: UIW_WINDOW(left, top, width, height, woFlags, WOAF_NO_FLAGS)
{
	woStatus |= WOS_OWNERDRAW;
	woAdvancedFlags |= WOAF_NON_CURRENT;
}

EVENT_TYPE LINE_CHART::DrawItem(const UI_EVENT &event, EVENT_TYPE ccode)
{
	display->VirtualGet(screenID, trueRegion);

	UIW_WINDOW::DrawItem(event, ccode);

	// Initialize data for line graph.
	static int redLine[] = { 25, 20, 37, 48, 86, 79, 93, 0 };
	static int greenLine[] = { 20, 30, 35, 58, 50, 65, 80, 0 };
	const int left = trueRegion.left,
		bottom = trueRegion.bottom,
		width = 4 * display->cellWidth;

	// Create the graph lines for lineWindow.
	int x;
	for (x = 0; redLine[x+1]; x++)
		display->Line(screenID, left + x * width, bottom - MiniConvertY(redLine[x]),
			left + (x + 1) * width, bottom - MiniConvertY(redLine[x+1]),
			&graphPalette[7], 1, FALSE, &clip);

	for (x = 0; greenLine[x+1]; x++)
		display->Line(screenID, left + x * width, bottom - MiniConvertY(greenLine[x]),
			left + (x + 1) * width, bottom - MiniConvertY(greenLine[x+1]),
			&graphPalette[8], 1, FALSE, &clip);

	// Draw the associated text.
	static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", ZIL_NULLP(ZIL_ICHAR) };
	for (x = 0; month[x]; x++)
		display->Text(screenID, left + x * width + 1, bottom -
			display->cellHeight + display->preSpace + display->postSpace,
			month[x], &textPalette, -1, FALSE, FALSE, &clip);

	display->VirtualPut(screenID);
	return (TRUE);
}

BAR_CHART::BAR_CHART(int left, int top, int width, int height, WOF_FLAGS woFlags)
	: UIW_WINDOW(left, top, width, height, woFlags, WOAF_NO_FLAGS)
{
	woStatus |= WOS_OWNERDRAW;
	woAdvancedFlags |= WOAF_NON_CURRENT;
}

EVENT_TYPE BAR_CHART::DrawItem(const UI_EVENT &event, EVENT_TYPE ccode)
{
	// Set up the bar chart data.
	static const int BAR_COUNT = 6;
	static int height[BAR_COUNT] = { 67, 46, 88, 81, 65, 32 };
	static char *year[BAR_COUNT] = { "1985", "1986", "1987", "1988", "1989", "1990" };

	// Virtualize the display.
	display->VirtualGet(screenID, trueRegion);

	// Draw the outer border and clear the region.
	UIW_WINDOW::DrawItem(event, ccode);

	// Compute the draw region then draw the bars.
	int bottom = trueRegion.top + MiniConvertY(100);
	int spacing = 2 * display->cellWidth;
	int barWidth = (trueRegion.Width() - (BAR_COUNT + 1) * spacing) / BAR_COUNT;
	int left = trueRegion.left + spacing;
	int i;
	for (i = 0; i < BAR_COUNT; i++)
	{
		display->Rectangle(screenID, left, bottom - MiniConvertY(height[i]),
			left + barWidth - 1, bottom, &graphPalette[i], 1, TRUE, FALSE, &clip);
		display->Text(screenID, left, bottom + display->cellHeight, year[i],
			&graphPalette[6], -1, FALSE, FALSE, &clip);
		left += barWidth + spacing;
	}

	// Un-virtualize the display.
	display->VirtualPut(screenID);
	return (TRUE);
}

PIE_CHART::PIE_CHART(int left, int top, int width, int height, WOF_FLAGS flags)
	: UIW_WINDOW(left, top, width, height, flags, WOAF_NO_FLAGS)
{
	woStatus |= WOS_OWNERDRAW;
	woAdvancedFlags |= WOAF_NON_CURRENT;
}

EVENT_TYPE PIE_CHART::DrawItem(const UI_EVENT &event, EVENT_TYPE ccode)
{
	display->VirtualGet(screenID, trueRegion);

	UIW_WINDOW::DrawItem(event, ccode);

	static int sectors[] = { 0, 56, 112, 133, 210, 240, 360, 0 };

	// The following values are dependant on the above values.
	//  cosX[i] = 100 * cos((sector[i] + sector[i + 1]) / 2);
	//  cosX[i] = 100 * sin((sector[i] + sector[i + 1]) / 2);
	static int cosX[] = { 88, 10, -54, -99, -71,  50 };
	static int sinY[] = { 47, 99,  84,  15, -71, -87 };

	static char *label[] = { "Rent", "Food", "Clothing", "Auto", "Other", "Entertainment" };
	UI_POSITION center;
	center.column = trueRegion.Width() / 2;
	center.line = trueRegion.Height() / 2;

	// Draw shadow, scaled to size of pie graph.
	display->Ellipse(screenID, trueRegion.left + center.column,
		trueRegion.top + center.line - display->cellHeight / 2 + center.line/8, 0, 360,
		center.column/2, center.line/2, &graphPalette[6], TRUE, FALSE, &clip);

	int sector;
	for (sector = 0; sectors[sector+1]; sector++)
	{
		display->Ellipse(screenID, trueRegion.left + center.column, trueRegion.top + center.line -
			display->cellHeight / 2, sectors[sector], sectors[sector+1], center.column/2,
			center.line/2, &graphPalette[sector], TRUE, FALSE, &clip);
		long left = (long)cosX[sector] * center.column / 2 / 100;
		left += left > 0 ? display->cellWidth + trueRegion.left + center.column:
			-(display->TextWidth(label[sector], screenID, font) + display->cellWidth)
			+ trueRegion.left + center.column;
		long top = (long)sinY[sector] * center.line / 2 / 100;
		top = top > 0 ? trueRegion.top + center.line - top - display->TextHeight(label[sector], screenID, font) -
			display->cellHeight / 2 : trueRegion.top + center.line - top + display->cellHeight / 2;
		display->Text(screenID, (int)left, (int)top, label[sector],
			&graphPalette[sector], -1, FALSE, FALSE, &clip, font);
	}

	display->VirtualPut(screenID);
	return (TRUE);
}

int UI_APPLICATION::Main(void)
{
	// The UI_APPLICATION constructor automatically initializes the
	// display, eventManager, and windowManager variables.

	// This line fixes linkers that don't look for main in the .LIBs.
	UI_APPLICATION::LinkMain();

	// Check for graphics display.
	if (display->isText)
		return (0);

	// Add the windows to the window manager.
	*windowManager
		+ &(*UIW_WINDOW::Generic(25, 3, 45, 16, "BAR")
			+ new BAR_CHART(20, 10, 380, 100 + display->cellHeight,
				WOF_MINICELL | WOF_BORDER))
		+ &(*UIW_WINDOW::Generic(15, 12, 60, 8, "SIZABLE PIE CHART")
			+ new PIE_CHART(0, 0, 0, 0, WOF_NON_FIELD_REGION))
		+ &(*UIW_WINDOW::Generic(34, 7, 52, 8, "PIE CHART")
			+ new PIE_CHART(0, 0, 50, 7))
		+ &(*UIW_WINDOW::Generic(7, 2, 30, 14, "LINE CHART")
			+ new LINE_CHART(20, 10, 240, 100, WOF_MINICELL | WOF_BORDER));

	// Process user responses.
	UI_APPLICATION::Control();

	return (0);
}
