/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

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



#include "VApplication.h"
VApplication *theApp = nil;

#include "VMouseTracker.h"
#include "VLayoutAttachment.h"
#include "CWindowMenuAttachment.h"
#include "CClickCheckAttachment.h"

#include "v/VDisplay.h"
#include "v/VWindow.h"
#include "v/VChooseFile.h"
#include "v/VMenu.h"

#include <PP_DebugMacros.h>

#include <LGrowZone.h>
#include <URegistrar.h>
#include <UEnvironment.h>
#include <LMenuBar.h>
#include <LMenu.h>
#include <LClipboard.h>

#include <UAttachments.h>

#include <UStandardDialogs.h>
#if PP_StdDialogs_Option == PP_StdDialogs_Conditional
	#include <UConditionalDialogs.h>
#endif

#include <UControlRegistry.h>

#include <LDebugStream.h>

#include <LWindow.h>
#include <LDialogBox.h>
#include <LTabGroup.h>

#include <Appearance.h>

#include <UThread.h>
#include <LCleanupTask.h>

#include "CColorView.h"
#include "CColorViewImp.h"

#include "CDataBrowser.h"
#include "CDataBrowserImp.h"

extern "C" {
#include "init.h"
#include "long_char.h"
#include "stream.h"
#include "task.h"
#include "utils.h"
#include "machine/v_types.h"
#include "memory_debug.h"

void SetSleepTime(UInt32 inSleepTime);
void DisplayAlert(char *msg);
void QuitApplication();
void call__main();
}

#include "CYieldRepeater.h"

CYieldRepeater *sCYieldRepeater = nil;
UInt32 CYieldRepeater::curTick;
bool CYieldRepeatersExitLoop = false;
bool *CYieldRepeater::sExitLoop = &CYieldRepeatersExitLoop;
int CYieldRepeater::sGetMouseEvent = 0;
LThread *CYieldRepeater::sGoBack = 0;
long free_mem_size;

bool CClickCheckAttachment::sClickHandled = 0;

void(*v_open_file_callback)(const char*) = NULL;


// ===========================================================================
//		¥ Utility Routine
// ===========================================================================

extern "C" {

void
SetSleepTime(UInt32 inSleepTime)
{
	theApp->SetSleepTime(inSleepTime==0 ? 0 : appBusyLv ? 2 : 12);
}

void
DisplayAlert(char *msg)
{
	Str255 pmsg;
	c2pstrcpy(pmsg, msg);
	::ParamText(pmsg,"\p","\p","\p");
	::StopAlert(ALRT_Msg, nil);
}

void DeleteAllServerMaster();

Boolean atQuitApp_flag = 0;

void
QuitApplication()
{
	if ( atQuitApp_flag )
		return;
	printf("QuitApplication...\n");
	if ( theApp->GetState() != programState_Quitting )
		theApp->DoQuit();
	if ( LThread::GetCurrentThread() != LThread::GetMainThread() )
			// switch to main thread (main thread will dispose every thread)
		LThread::Yield(LThread::GetMainThread());	
	else {
			// clean up every thing here (i.e. main thread) and continue exit
		DeleteAllServerMaster();
		LCleanupTask::CleanUpAtExit();
	}
}


} // extern "C"

// ===========================================================================
//	¥ main
// ===========================================================================

int
main()
{
#if __MACH__
	int fd = open("/tmp/COSMOS.log", O_CREAT | O_WRONLY, 0666);
	ftruncate(fd, 0);
	dup2(fd, 1);
	dup2(fd, 2);
	close(fd);
#endif

	UDebugging::SetDebugThrow(debugAction_Nothing);
	UDebugging::SetDebugSignal(debugAction_Nothing);

		// Normal initializations
	InitializeHeap(5);
	UQDGlobals::InitializeToolbox();
	UEnvironment::InitEnvironment();
	
	LThread::Initialize();

		// Install a GrowZone to catch low-memory situations	
	LGrowZone* theGZ = new LGrowZone(20000);

	atexit(QuitApplication);

    if ( UEnvironment::IsRunningOSX() ) {
            Gestalt(gestaltPhysicalRAMSize, &free_mem_size);
            free_mem_size -= (512<<20)*(1.0-exp(-free_mem_size/float(784<<20)));
    } else {
            free_mem_size = FreeMem()-(10<<20);
    }

	theApp = new VApplication;
	theApp->Run();
	
		// Make sure async tasks get cleaned up. This call is VERY IMPORTANT.
	atQuitApp_flag = 1;
	DeleteAllServerMaster();
	LCleanupTask::CleanUpAtExit();
	
	return 0;
}



// ---------------------------------------------------------------------------
//	¥ VApplication									[public]
// ---------------------------------------------------------------------------
//	Application object constructor

VApplication::VApplication()
{
		// Register ourselves with the Appearance Manager
	if (UEnvironment::HasFeature(env_HasAppearance)) {
		::RegisterAppearanceClient();
	}

	RegisterClasses();
	
		// Preload facilities for the Standard Dialogs
	PP_StandardDialogs::Load();
	
		// Require at least Navigation Services 1.1. See comments
		// above SetTryNavServices in UConditionalDialogs.cp for why
		// you might wish to do this.
#if PP_StdDialogs_Option == PP_StdDialogs_Conditional
	UConditionalDialogs::SetTryNavServices(0x01108000);
#endif

	AddAttachment(new LClipboard);
	AddAttachment(new VLayoutAttachment);
	AddAttachment(new CWindowMenuAttachment);
	AddAttachment(new CClickCheckAttachment);
	
	sCYieldRepeater = new CYieldRepeater(thTicksYield);
	VMouseTracker* mt = new VMouseTracker;
	mt->StartRepeating();
}


// ---------------------------------------------------------------------------
//	¥ ~VApplication									[public, virtual]
// ---------------------------------------------------------------------------
//	Application object destructor

VApplication::~VApplication()
{
	VDisplay::get_main_display()->destroy();

		// Clean up after Standard Dialogs
	PP_StandardDialogs::Unload();
}


// ---------------------------------------------------------------------------
//	¥ StartUp										[protected, virtual]
// ---------------------------------------------------------------------------
//	Perform an action in response to the Open Application AppleEvent.
//	Here, issue the New command to open a window.

extern "C" {
void check_endian();
void init_msequence();
void check_endian();
void init_longchar();
void init_utils();
void init_netutils();
void init_event();
void init_resolve();
void init_wct();
void init_blacklist();
int _main(int,char**);
char *get_path_from_mac_fs(FSSpec inMacFSSpec, Boolean isFolder);
}

void
VApplication::DoPreferences()
{
	void v_setup_menu_choosed();
	v_setup_menu_choosed();
}

void
VApplication::StartUp()
{

	check_endian();
	init_task(INI_DONTWAITCHI);
	init_msequence();
	check_endian();
	init_tick();
	init_d_alloc();
	init_longchar();
	init_wct();
	init_stream();
	init_utils();
	init_netutils();
	init_resolve();

	call__main();
}

// ---------------------------------------------------------------------------
//	¥ DoReopenApp									[protected, virtual]
// ---------------------------------------------------------------------------
//	Support the Finder's "re-open application" ('rapp') Apple Event. From
//	Apple TechNote 1102 (on Mac OS 8):
//
//		The Finder now sends a 're-open application' Apple event ('rapp') to
//		applications when the application is already running and it is opened
//		from one of the Finder's windows (either by a double click or by
//		selecting the application and choosing the Open command). Applications
//		receiving a 'rapp' event (when they do not have any windows open) should
//		open a new untitled document just as they would when processing an 'oapp'
//		event.

void
VApplication::DoReopenApp()
{
	
}


// ---------------------------------------------------------------------------
//	¥ ObeyCommand									[public, virtual]
// ---------------------------------------------------------------------------
//	Respond to Commands. Returns true if the Command was handled, false if not.

Boolean
VApplication::ObeyCommand(
	CommandT	inCommand,
	void*		ioParam)
{
	Boolean		cmdHandled = true;	// Assume we'll handle the command
	extern VCustomizedMenuBar *menu_no_window;

	switch (inCommand) {
	  case cmd_About:
		void v_about_menu_choosed();
		v_about_menu_choosed();
		break;
	
	  default:
		if ( menu_no_window && inCommand < 0 && 
				menu_no_window->menu_choosed(inCommand) )
			cmdHandled = true;
		else
			cmdHandled = LApplication::ObeyCommand(inCommand, ioParam);
		break;
	}
	
	return cmdHandled;
}





// ---------------------------------------------------------------------------
//	¥ FindCommandStatus								[public, virtual]
// ---------------------------------------------------------------------------
//	Determine the status of a Command for the purposes of menu updating.

void
VApplication::FindCommandStatus(
	CommandT	inCommand,
	Boolean&	outEnabled,
	Boolean&	outUsesMark,
	UInt16&		outMark,
	Str255		outName)
{
	extern bool MenuFlag_about;
	extern bool MenuFlag_setup;
	extern VCustomizedMenuBar *menu_no_window;
	
	switch (inCommand) {
	  case cmd_About:
		outEnabled = MenuFlag_about;
		break;

	  case cmd_Preferences:
		outEnabled = MenuFlag_setup;
		break;

	  default:
		short flag;
		if ( menu_no_window && inCommand < 0 &&
				menu_no_window->get_menu_flag(inCommand, &flag) ) {
			outEnabled = flag & VMF_ENABLED;
			outUsesMark = flag & VMF_CHECKED;
			if ( outUsesMark )
				outMark = checkMark;
		}
		else
			LApplication::FindCommandStatus(inCommand, outEnabled,
											outUsesMark, outMark, outName);
		break;
	}

}


// ---------------------------------------------------------------------------
//	¥ AttemptQuitSelf
// ---------------------------------------------------------------------------

extern "C" int vobject_quit();

Boolean
VApplication::AttemptQuitSelf(
	SInt32	/* inSaveOption */)
{

	if ( mState != programState_Quitting )
		return vobject_quit();
	return true;
}


// ---------------------------------------------------------------------------
//	¥ RegisterClasses								[protected]
// ---------------------------------------------------------------------------
//	To reduce clutter within the Application object's constructor, class
//	registrations appear here in this seperate function for ease of use.

void
VApplication::RegisterClasses()
{

		// Register core PowerPlant classes.
	RegisterClass_(LWindow);

	RegisterClassID_( CColorViewImp,	CColorView::imp_class_ID );
	RegisterClassID_( CDataBrowserImp,	CDataBrowser::imp_class_ID );

		// Register the Appearance Manager/GA classes. You may want
		// to remove this use of UControlRegistry and instead perform
		// a "manual" registration of the classes. This cuts down on
		// extra code being linked in and streamlines your app and
		// project. However, use UControlRegistry as a reference/index
		// for your work, and ensure to check UControlRegistry against
		// your registrations each PowerPlant release in case
		// any mappings might have changed.
		
	UControlRegistry::RegisterClasses();

}


// ---------------------------------------------------------------------------
//	¥ OpenDocument								  [public]
// ---------------------------------------------------------------------------
//	Open a Document specified by an FSSpec

void
VApplication::OpenDocument(
	FSSpec*	inMacFSSpec)
{
	char * path = get_path_from_mac_fs(*inMacFSSpec,false);
	if ( v_open_file_callback )
		v_open_file_callback(path);
	d_f_ree(path);
}


// ---------------------------------------------------------------------------
//	¥ ChooseDocument								[protected, virtual]
// ---------------------------------------------------------------------------
void
VApplication::ChooseDocument()
{
	PP_StandardDialogs::LFileChooser	chooser;
	
		// Open any/all TEXT files
	
	NavDialogOptions*	options = chooser.GetDialogOptions();
	if (options != nil) {
		options->dialogOptionFlags =	kNavDefaultNavDlogOptions
										+ kNavSelectAllReadableItem;
	}

	if (chooser.AskOpenFile(LFileTypeList(ResType_Text))) {
		AEDescList		docList;
		chooser.GetFileDescList(docList);
		SendAEOpenDocList(docList);
	}
}
