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

// The ZAF 5 Development Team:
//   Larry Macfarlane
//   Richard Paulsen
//   Mike Redd
//   Blake Jones
//   Michael Stone
//   James McPherson
//   David Frye
//   Brian Christiansen

#include <z_devall.hpp>
#include <z_win.hpp>
#include <z_app.hpp>
#include <z_store.hpp>
#include <z_error.hpp>
#include <z_help.hpp>
#define ZAF_APPLICATION_INFO
#include "gbl_def.cpp"

// ----- ZafApplication -----------------------------------------------------

#if defined(ZAF_INDEPENDENT)
bool zafBignumData_bound;
bool zafIntegerData_bound;
bool zafRealData_bound;
bool zafStringData_bound;
bool zafUTimeData_bound;
ZafWindowObject *zafCursor_activeObject;
bool zafCursor_upToDate;
bool zafCursor_initialized;
bool zafHelpTips_processDisplay;
char zafMetaDisplay_inCopy;
bool zafMouse_initialized;

extern ZafListBlock *ownerWindowList;

static void InitializeGlobals(void)
{
	ownerWindowList = ZAF_NULLP(ZafListBlock);
	zafCodeSet = ZAF_NULLP(ZafCodeSetDataStub);
	zafDataManager = ZAF_NULLP(ZafDataManager);
	zafDisplay = ZAF_NULLP(ZafDisplay);
	zafEventManager = ZAF_NULLP(ZafEventManager);
	zafLanguageManager = ZAF_NULLP(ZafLanguageManager);
	zafLocale = ZAF_NULLP(ZafLocaleData);
	zafWindowManager = ZAF_NULLP(ZafWindowManager);

	ZafFormatData::formatTableEntries = 0;
	ZafFormatData::parseTableEntries = 0;
	ZafI18nData::staticModule = ZAF_NULLP(ZafI18nData::ZafStaticModule);
	ZafLocaleData::canonicalLocale = ZAF_NULLP(ZafLocaleData);

	zafBignumData_bound = false;
	zafIntegerData_bound = false;
	zafRealData_bound = false;
	zafStringData_bound = false;
	zafUTimeData_bound = false;
	zafCursor_activeObject = ZAF_NULLP(ZafWindowObject);
	zafCursor_upToDate = true;
	zafCursor_initialized = false;
	zafHelpTips_processDisplay = false;
	zafMetaDisplay_inCopy = 0;
	zafMouse_initialized = false;
}
#endif

ZafApplication::ZafApplication(int _argc, char **_argv) :
	argc(0), argv(ZAF_NULLP(ZafIChar *)), error(ZAF_ERROR_NONE)
{
#if defined(ZAF_INDEPENDENT)
	// This call is used to reinitialized global variables each time
	// the application is run.  (Otherwise the program would need to
	// be re-loaded for globals to be reinitialized.)
	InitializeGlobals();
#endif

	// Initialize the data manager.
	zafDataManager = new ZafDataManager;

	// Call I18nAllocate() to reflect changes based on the display.
	ZafI18nData::I18nAllocate();

	// Allocate the display.
	zafDisplay = new ZafScreenDisplay(_argc, _argv);

#if defined(ZAF_INDEPENDENT)
	// If the display didn't initialize, exit gracefully.
	if (!((ZafScreenDisplay *)zafDisplay)->initialized)
	{
		SetError(ZAF_ERROR_CONSTRUCTOR);
		printf("Couldn't initialize the screen display.\n");
		return;
	}
#endif

	// Allocate the arguments. While this code technically allows argv[0] to
	// be NULL (which violates the language definition), this condition
	// should not occur because the data passed to us should always be valid
	// (i.e., _argv[0] should contain the program name). If the data is
	// invalid, it is likely a compiler error, or at least is a problem
	// that cannot be fixed here so we do our best to work with it by allowing
	// for the invalid state.

	// Initialize argc here since an error may have occurred before this point.
	argc = _argc;
	argv = new ZafIChar *[argc + 1];
	for (int i = 0; i < argc; i++)
	{
#if defined(USE_ZAF_CODE_SET_DATA)
		argv[i] = zafCodeSet->ConvertToZafString(_argv[i]);
#else
		argv[i] = strdup(_argv[i]);
#endif
	}
	argv[argc] = ZAF_NULLP(ZafIChar);

	// Create search path from the program path.
#if defined(USE_ZAF_SEARCH_PATH)
	if (argv[0])
		zafSearchPath = new ZafPath(argv[0], true);
	else
		zafSearchPath = new ZafPath(ZAF_NULLP(ZafIChar), true);
#endif

#if defined(USE_ZAF_PERSISTENCE)
	// Call ResetI18n() to reflect changes based on the storage.
	zafI18nStorage = new ZafStorage(ZAF_ITEXT("i18n.znc"), ZAF_FILE_READ);
	if (zafI18nStorage->Error())
	{
		delete zafI18nStorage;
		zafI18nStorage = ZAF_NULLP(ZafFileSystem);
	}
	else
	{
		zafDataPersistence = new ZafDataPersistence(zafI18nStorage, 0);
		zafDataPersistence->AddDataConstructor(ZafLanguageData::className, ZafLanguageData::classID, &ZafLanguageData::Read);
		zafDataPersistence->AddDataConstructor(ZafLocaleData::className, ZafLocaleData::classID, &ZafLocaleData::Read);
		zafDataPersistence->AddDataConstructor(ZafCodeSetData::className, ZafCodeSetData::classID, &ZafCodeSetData::Read);
		ZafI18nData::ResetI18n();
	}
#endif

	// Allocate the event manager.
	zafEventManager = new ZafEventManager;
	InitMouse();
	InitKeyboard();
	zafEventManager->Add(new ZafCursor);
	InitHelpTips();

	// Allocate the window manager.
	zafWindowManager = new ZafWindowManager;

	// Allocate the error system.
	InitErrorSystem();

	// Initialize the global pointer to the application object.
	zafApplication = this;
}

ZafApplication::~ZafApplication(void)
{
	// Free the arguments.
	if (argc)
	{
		for (int i = 0; i < argc; i++)
			delete []argv[i];
		delete []argv;
	}

	// Free the help and error systems.
#if defined(USE_ZAF_ERROR_SYSTEM)
	if (zafHelpSystem)
		delete zafHelpSystem;
	zafHelpSystem = ZAF_NULLP(ZafHelpSystem);
	if (zafErrorSystem)
		delete zafErrorSystem;
	zafErrorSystem = ZAF_NULLP(ZafErrorStub);
#endif

	// Free the associated high-level managers.
	if (zafWindowManager)
		delete zafWindowManager;
	zafWindowManager = ZAF_NULLP(ZafWindowManager);
	if (zafEventManager)
		delete zafEventManager;
	zafEventManager = ZAF_NULLP(ZafEventManager);

#if defined(USE_ZAF_PERSISTENCE)
	// Free the persistence modules.
	if (zafObjectPersistence)
		delete zafObjectPersistence;
	if (zafDataPersistence == zafObjectPersistence)
		zafDataPersistence = ZAF_NULLP(ZafDataPersistence);
	zafObjectPersistence = ZAF_NULLP(ZafObjectPersistence);
	if (zafDataPersistence)
		delete zafDataPersistence;
	zafDataPersistence = ZAF_NULLP(ZafDataPersistence);
#endif

#if defined(USE_ZAF_SEARCH_PATH)
	if (zafSearchPath)
		delete zafSearchPath;
	zafSearchPath = ZAF_NULLP(ZafPath);
#endif

#if defined(USE_ZAF_PERSISTENCE)
	if (zafI18nStorage)
		delete zafI18nStorage;
	zafI18nStorage = ZAF_NULLP(ZafFileSystem);
#endif

	ZafI18nData::I18nFree(true);

	// zafDataManager must be deleted after I18nFree() because it does
	// destroy its chilren if their notify count is 0.  It also must be
	// deleted before the display.
	delete zafDataManager;
	zafDataManager = ZAF_NULLP(ZafDataManager);

	if (zafDisplay)
		delete zafDisplay;
	zafDisplay = ZAF_NULLP(ZafDisplay);
}

ZafEventType ZafApplication::Control(ZafQFlags flags)
{
	// Make sure there is a window attached to the window manager.
	if (!zafWindowManager->First())
		return (S_NO_OBJECT);

	// Wait for user response.
	ZafEventStruct event;
	ZafEventType ccode = 0;
	do
	{
		if (zafEventManager->Get(event, flags) == 0)
			ccode = zafWindowManager->Event(event);
	} while (ccode != S_EXIT && ccode != S_NO_OBJECT);

	// Return the final code.
	return (ccode);
}

void ZafApplication::Flush(void)
{
	ZafEventStruct event;
	while (!zafEventManager->Get(event))
		zafWindowManager->Event(event);
}

ZafError ZafApplication::SetError(ZafError setError) 
{ 
	error = setError; 
	return (error); 
}

// ---- Initialization methods ----------------------------------------------

void ZafApplication::InitErrorSystem()
{
#if defined(USE_ZAF_ERROR_SYSTEM)
	zafErrorSystem = new ZafErrorSystem;
#endif
}

void ZafApplication::InitHelpTips()
{
#if defined(USE_ZAF_HELP_TIPS)
	zafEventManager->Add(new ZafHelpTips(D_ON, ZAF_HELPTIPS_BOTH));
#endif
}

void ZafApplication::InitKeyboard()
{
#if defined(USE_ZAF_KEYBOARD)
	zafEventManager->Add(new ZafKeyboard);
#endif
}

void ZafApplication::InitMouse()
{
#if defined(USE_ZAF_MOUSE)
	zafEventManager->Add(new ZafMouse);
#endif
}

// Some linkers do not look for the main or WinMain in a library. 
// Invoking the LinkMain function within the application forces the
// appropriate module containing the main or WinMain to be properly
// linked.  RTOS platforms typically do not need to be concerned with
// this, as they will normally compile the startup function into their
// application.
#if defined(ZAF_RTOS)
void ZafApplication::LinkMain(void)
{
}
#endif

