//	Zinc Application Framework - Z_PRGRSS.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_prgrss.hpp>
#include <z_string.hpp>
#define ZAF_PROGRESS_BAR_INFO
#include "gbl_def.cpp"

// ----- ZafProgressBar -----------------------------------------------------

ZafProgressBar::ZafProgressBar(int left, int top, int width, int height,
	ZafScrollData *zProgress) :
	ZafWindowObject(left, top, width, height),
	textStyle(ZAF_PROGRESS_TEXT_VALUE), progressType(ZAF_PROGRESS_HORIZONTAL), 
	progressStyle(ZAF_PROGRESS_NATIVE), progressData(ZAF_NULLP(ZafScrollData))
{
	// Initialize the progress information.
	SetOSDraw(false);
	SetNoncurrent(true);
		
	// Initialize the progress data.
	if (zProgress)
		SetProgressData(zProgress);
	else
		SetProgressData(new ZafScrollData(0, 100, 100));
}

ZafProgressBar::ZafProgressBar(const ZafProgressBar &copy):
	ZafWindowObject(copy),
	textStyle(copy.textStyle), progressType(copy.progressType), 
	progressStyle(copy.progressStyle), progressData(ZAF_NULLP(ZafScrollData))
{
	// Initialize the progress information.
	if (copy.progressData && copy.progressData->Destroyable())
		SetProgressData(new ZafScrollData(*copy.progressData));
	else if (copy.progressData)
		SetProgressData(copy.progressData);
}

ZafProgressBar::~ZafProgressBar(void)
{
	// Remove the data notification.
	if (progressData)
		progressData->SubtractNotification(this, (ZafUpdateFunction)Update);

	// Restore the progress information.
	if (progressData && progressData->Destroyable() && progressData->NotifyCount() == 0)
		delete progressData;
}

ZafWindowObject *ZafProgressBar::Duplicate(void)
{
	return (new ZafProgressBar(*this));
}

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

	switch (ccode)
	{
		case N_SIZE:
			ccode = ZafWindowObject::Event(event);
			Redisplay();
			break;
		default:
			ccode = ZafWindowObject::Event(event);
	}

	return (ccode);
}

bool ZafProgressBar::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafWindowObject::IsA(compareID));
}

bool ZafProgressBar::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafWindowObject::IsA(compareName));
}

#if defined(ZAF_DEFAULT_DRAW)
static ZafIChar _valueFormat[] = { '%', 'd', '\0' };
static ZafIChar _percentFormat[] = { '%', 'd', '%', '%', '\0' };

ZafEventType ZafProgressBar::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	// Draw the border.
	if (bordered)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, state));
		display->Rectangle(drawRegion, 1, false);
		drawRegion--;
	}

	// Draw the shadow.
	if (progressStyle == ZAF_PROGRESS_NATIVE || progressStyle == ZAF_PROGRESS_INDENTED)
		DrawShadow(drawRegion, -ZAF_BORDER_SIZE, ccode);
	else if (progressStyle == ZAF_PROGRESS_RAISED)
		DrawShadow(drawRegion, ZAF_BORDER_SIZE, ccode);
	
	// Calculate the percentage of the bar that needs to be filled.
	long percentDone;
	if (progressData)
	{
		long min = progressData->Minimum();
		long max = progressData->Maximum();
		if (max > min)
			percentDone = (100 * (progressData->Current() - min)) / (max - min);
		else
			percentDone = 100;
	}
	else
		percentDone = 0;

	ZafRegionStruct unfinished = drawRegion;
	ZafRegionStruct finished = drawRegion;
	if (progressType == ZAF_PROGRESS_HORIZONTAL)
	{
		// Progress goes left to right.
		finished.right = (int)(finished.left + drawRegion.Width() * percentDone / 100 - 1);
		unfinished.left = finished.right + 1;
	}
	else
	{
		// Progress goes bottom to top.
		unfinished.bottom = (int)(unfinished.top + drawRegion.Height() * (100 - percentDone) / 100 - 1);
		finished.top = unfinished.bottom + 1;
	}

	// Draw the background.
	ZafPaletteStruct foreground = LogicalPalette(ZAF_PM_TEXT, state);
	ZafPaletteStruct background = LogicalPalette(ZAF_PM_BACKGROUND, state);
	if (unfinished.left <= unfinished.right)
	{
		display->SetPalette(background);
		display->Rectangle(unfinished, 0, true);
	}
	if (finished.left <= finished.right)
	{
		display->SetPalette(foreground);
		display->Rectangle(finished, 0, true);
	}

	// Determine the draw text.
	if (textStyle != ZAF_PROGRESS_TEXT_NONE)
	{
		// Determine the display format.
		ZafIChar text[8];
		sprintf(text, (textStyle == ZAF_PROGRESS_TEXT_PERCENT) ? _percentFormat : _valueFormat,
			(textStyle == ZAF_PROGRESS_TEXT_PERCENT) ? percentDone : progressData->Current() );

		// Draw the text.
		if (unfinished.left <= unfinished.right)
		{
			display->SetPalette(foreground);
			display->SetClipRegion(unfinished);
			display->Text(drawRegion, text, -1, ZAF_HZ_CENTER, ZAF_VT_CENTER);
		}
		if (finished.left <= finished.right)
		{
			display->SetPalette(background);
			display->SetClipRegion(finished);
			display->Text(drawRegion, text, -1, ZAF_HZ_CENTER, ZAF_VT_CENTER);
		}
	}

	// Return the control code.
	EndDraw();
	return (ccode);
}
#endif

const ZafPaletteStruct *ZafProgressBar::MapClassPalette(ZafPaletteType type, ZafPaletteState state)
{
	const ZafPaletteStruct *palette = MapPalette(defaultPaletteMap, type, state);
	return (palette ? palette : ZafWindowObject::MapClassPalette(type, state));
}

bool ZafProgressBar::SetAcceptDrop(bool )
{
	// acceptDrop is false for this class.
	acceptDrop = false;
	return (acceptDrop);
}

ZafLogicalColor ZafProgressBar::SetBackgroundColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the new entry.
	ZafPaletteStruct backgroundPalette = userPaletteData->GetPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE);
	backgroundPalette.colorBackground = backgroundPalette.colorForeground = color;
	backgroundPalette.monoBackground = backgroundPalette.monoForeground = mono;
	userPaletteData->AddPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE, backgroundPalette);

	// Return the current color.
	return (color);
}

bool ZafProgressBar::SetCopyDraggable(bool )
{
	// copyDraggable is false for this class.
	copyDraggable = false;
	return (copyDraggable);
}

bool ZafProgressBar::SetFocus(bool )
{
	// focus is false for this class.
	focus = false;
	return (focus);
}

ZafLogicalFont ZafProgressBar::SetFont(ZafLogicalFont font)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add two new entries, one for foreground, one for background.
	ZafPaletteStruct backgroundPalette = userPaletteData->GetPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE);
	backgroundPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE, backgroundPalette);

	ZafPaletteStruct textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED);
	textPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED, textPalette);

	// Return the current font.
	return (font);
}

const ZafIChar *ZafProgressBar::SetHelpContext(const ZafIChar *)
{
	// helpContext is empty for this class.
	return (ZAF_NULLP(ZafIChar));
}

bool ZafProgressBar::SetLinkDraggable(bool )
{
	// linkDraggable is false for this class.
	linkDraggable = false;
	return (linkDraggable);
}

bool ZafProgressBar::SetMoveDraggable(bool )
{
	// moveDraggable is false for this class.
	moveDraggable = false;
	return (moveDraggable);
}

bool ZafProgressBar::SetNoncurrent(bool )
{
	// noncurrent is true for this class.
	noncurrent = true;
	return (noncurrent);
}

bool ZafProgressBar::SetParentDrawBorder(bool )
{
	// parentDrawBorder is false for this class.
	parentDrawBorder = false;
	return (parentDrawBorder);
}

bool ZafProgressBar::SetParentDrawFocus(bool )
{
	// parentDrawFocus is false for this class.
	parentDrawFocus = false;
	return (parentDrawFocus);
}

bool ZafProgressBar::SetParentPalette(bool )
{
	// parentPalette is false for this class.
	parentPalette = false;
	return (parentPalette);
}

bool ZafProgressBar::SetProgressData(ZafScrollData *tProgressData)
{
	// Remove the data notification.
	if (progressData)
		progressData->SubtractNotification(this, (ZafUpdateFunction)Update);

	// Reset the progress data.
	if (progressData && progressData != tProgressData &&
		progressData->Destroyable() && progressData->NotifyCount() == 0)
		delete progressData;
	progressData = tProgressData;

	// Add the data notification.
	if (progressData)
		progressData->AddNotification(this, (ZafUpdateFunction)Update);
	return (ZAF_ERROR_NONE);
}

ZafProgressStyle ZafProgressBar::SetProgressStyle(ZafProgressStyle zProgressStyle)
{
	// Make sure the attribute has changed.
	if (progressStyle != zProgressStyle && !screenID)
		progressStyle = zProgressStyle;

	// Return the current attribute.
	return (progressStyle);
}

ZafProgressType ZafProgressBar::SetProgressType(ZafProgressType zProgressType)
{
	// Make sure the attribute has changed.
	if (progressType != zProgressType && !screenID)
		progressType = zProgressType;

	// Return the current attribute.
	return (progressType);
}

bool ZafProgressBar::SetSelected(bool )
{
	// selected is false for this class.
	selected = false;
	return (selected);
}

ZafLogicalColor ZafProgressBar::SetTextColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the new entry.
	ZafPaletteStruct textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED);
	textPalette.colorBackground = textPalette.colorForeground = color;
	textPalette.monoBackground = textPalette.monoForeground = mono;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED, textPalette);

	// Return the current color.
	return (color);
}

ZafProgressTextStyle ZafProgressBar::SetTextStyle(ZafProgressTextStyle newTextStyle)
{
	// Make sure the attribute has changed.
	if (textStyle != newTextStyle)
		textStyle = newTextStyle;

	// Return the current attribute.
	return (textStyle);
}

ZafUserFunction ZafProgressBar::SetUserFunction(ZafUserFunction )
{
	// userFunction is null for this class.
	userFunction = ZAF_NULLF(ZafUserFunction);
	return (userFunction);
}

ZafError ZafProgressBar::OSSetProgress(void)
{
	// Redisplay the progress-bar data.
	RedisplayData();
	return (ZAF_ERROR_NONE);
}

ZafError ZafProgressBar::Update(ZafProgressBar *progressBar, ZafUpdateType type)
{
	ZafError retVal = ZAF_ERROR_INVALID;
	if (type & ZAF_UPDATE_OBJECT)
		retVal = progressBar->OSSetProgress();
	if (type & ZAF_UPDATE_DATA)
		retVal = (ZAF_ERROR_NONE);
	return (retVal);
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafProgressBar::ZafProgressBar(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafWindowObject(name, persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY)),
	progressData(ZAF_NULLP(ZafScrollData))
{
	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafProgressBar::ReadData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

ZafElement *ZafProgressBar::Read(const ZafIChar *name, ZafObjectPersistence &persist)
{
	return (new ZafProgressBar(name, persist));
}

void ZafProgressBar::ReadData(ZafObjectPersistence &persist)
{
	// Read the data.
	ZafUInt16 flag1;
	ZafIChar progressName[ZAF_MAXNAMELEN];
	ZafFile *file = persist.CurrentFile();
	*file >> flag1 >> progressName;
	progressType = (ZafProgressType)((int)flag1 & 0x000F);
	progressStyle = (ZafProgressStyle)(((int)flag1 & 0x00F0) >> 4);
	textStyle = (ZafProgressTextStyle)(((int)flag1 & 0x0F00) >> 8);

	// Read the progress data.
	SetProgressData(progressName[0] ? new ZafScrollData(progressName, persist) : new ZafScrollData(0, 100, 100));
}

void ZafProgressBar::Write(ZafObjectPersistence &persist)
{
	// Write the object.
	ZafWindowObject::Write(persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafProgressBar::WriteData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafProgressBar::WriteData(ZafObjectPersistence &persist)
{
	// Write the data.
	ZafUInt16 flag1 = 0;
	flag1 |= (ZafUInt16)(progressType & 0x000F);
	flag1 |= (ZafUInt16)((progressStyle & 0x000F) << 4);
	flag1 |= (ZafUInt16)((textStyle & 0x000F) << 8);
	const ZafIChar *progressName = progressData ? progressData->StringID() : ZAF_NULLP(ZafIChar);
	ZafFile *file = persist.CurrentFile();
	*file << flag1 << progressName;

	// Write the progress data.
	if (progressData)
		progressData->Write(persist);
}
#endif
