/* AbiSource Application Framework
 * Copyright (C) 1998 AbiSource, Inc.
 * Copyright (C) 1999 John Brewer DBA Jera Design
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * 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.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */
/*
	Platform: MacOS Classic & Carbon
	
	$Id: xap_MacApp.cpp,v 1.21 2001/11/05 22:37:29 hub Exp $	
*/

#include <string.h>

/* MacOS includes */
#ifndef XP_MAC_TARGET_QUARTZ
# include <QuickDraw.h>
#endif
#include <Folders.h>
#include <AppleEvents.h>
#include <MacWindows.h>
#include <Menus.h>
#include <Devices.h>
#include <Events.h>
#include <Appearance.h>
#ifdef XP_MAC_TARGET_MACOSX
# include <AEInteraction.h>
#endif

/* end */

#include "ut_string.h"
#include "ut_assert.h"
#include "ut_debugmsg.h"
#include "ut_MacFiles.h"
#include "xap_Args.h"
#include "xap_MacApp.h"
#include "xap_MacClipboard.h"
#include "xap_MacFrame.h"
#include "xap_MacFontManager.h"
#include "xap_MacTlbr_Icons.h"
#include "xap_MacTlbr_ControlFactory.h"
#include "xap_MacToolbar_Control.h"

#include "ev_MacMenu.h"


bool XAP_MacApp::m_NotInitialized = true;

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

XAP_MacApp::XAP_MacApp(XAP_Args * pArgs, const char * szAppName)
	: XAP_App(pArgs, szAppName), m_dialogFactory(this), m_controlFactory()
{
	if (m_NotInitialized) {
		InitializeMacToolbox ();
	}

	m_finished = false;
	m_pMacToolbarIcons = 0;
	m_pMacFontManager = NULL;
	m_pToolbarControl = new XAP_MacToolbar_Control ();
}

XAP_MacApp::~XAP_MacApp(void)
{
	DELETEP(m_pMacFontManager);
	DELETEP(m_pMacToolbarIcons);
	DELETEP(m_pToolbarControl);
}

bool XAP_MacApp::initialize(void)
{
	OSStatus err;

	// let our base class do it's thing.
	
	XAP_App::initialize();

	if (!_loadFonts())
		return false;

	// load only one copy of the platform-specific icons.

	m_pMacToolbarIcons = new AP_MacToolbar_Icons();
	
	// do anything else we need here...

	// _pClipboard = new AP_MacClipboard();

#if defined(USE_CARBON_EVENTS)
	EventTypeSpec eventTypes[1];
	eventTypes[0].eventClass = kEventClassCommand;
	eventTypes[0].eventKind = kEventCommandProcess;
	EventHandlerRef handlerRef;
	EventHandlerUPP handlerUPP = NewEventHandlerUPP (HandleCarbonMenus);
	err = ::InstallApplicationEventHandler (handlerUPP, 1, eventTypes, (void*)this, &handlerRef);
#endif

	m_pToolbarControl->show ();

	return true;
}

void XAP_MacApp::reallyExit(void)
{
	ExitToShell();
}

UT_sint32 XAP_MacApp::makeDirectory(const char * szPath, const UT_sint32 mode ) const
{ 
        /* FIXME: make this return something real.
	   The Unix version looks like this:
	      return mkdir(szPath, mode); 
	*/
        return 0;
}

XAP_DialogFactory * XAP_MacApp::getDialogFactory(void)
{
	return &m_dialogFactory;
}

XAP_Toolbar_ControlFactory * XAP_MacApp::getControlFactory(void)
{
	return &m_controlFactory;
}

const char * XAP_MacApp::getUserPrivateDirectory(void) 
{
        /* return a pointer to a static buffer */
	short vRefNum;
	long dirID;
	FSSpec dirSpec;
	OSErr err;
	Handle fullPath = NULL;
	short fpLen = 0;
	
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

        static char buf[PATH_MAX];
//        memset(buf,0,sizeof(buf));
		buf [0] = 0;
		
		/* change this later as MacOS don't really have user dir. use prefs dir
		   instead */
		::FindFolder (kOnSystemDisk, kPreferencesFolderType, true, &vRefNum, &dirID);
		err = ::FSMakeFSSpec (vRefNum, dirID, "\p", &dirSpec);
		UT_ASSERT (err == noErr);
		
		err = ::FSpGetFullPath (&dirSpec, &fpLen, &fullPath);
		UT_ASSERT (err == noErr);
		UT_ASSERT (fullPath != NULL);
		
		::HLock (fullPath);
		strncpy (buf, (char *)*fullPath, PATH_MAX);
		buf [fpLen] = 0;
		::HUnlock (fullPath); 
		::DisposeHandle (fullPath);
		fullPath = NULL;
		
		/* TODO: check buffer */
		strcat (buf, ":AbiSuite");
		
        return buf;                                     
}


bool XAP_MacApp::_loadFonts (void)
{
	m_pMacFontManager = new XAP_MacFontManager();
	UT_ASSERT(m_pMacFontManager);
	return true;
}



UT_uint32 XAP_MacApp::_getExeDir(char* /*pDirBuf*/, UT_uint32 /*iBufLen*/)
{
	UT_ASSERT (UT_NOT_IMPLEMENTED); 
	return 0;
}



void XAP_MacApp::InitializeMacToolbox ()
{
	OSStatus err;
	if (m_NotInitialized) {
		// This is REALLY the first thing to do
		// Even before any other Initialization. Note that this this is MacOS dependant
		// and application independent.
	#if XP_MAC_TARGET_CLASSIC
		// Initialize the Toolbox in Classic API

		::InitGraf(&qd.thePort);		// Toolbox Managers
		::InitFonts();
		::InitWindows();
		::InitMenus();
		::TEInit();
		::InitDialogs(nil);
		::MaxApplZone();

		::MoreMasters ();
		::MoreMasters ();
		::MoreMasters ();
		::MoreMasters ();
	#endif

	#if XP_MAC_TARGET_CARBON
		::MoreMasterPointers (4);
	#endif
		err = ::RegisterAppearanceClient ();
		m_NotInitialized = false;
	}
}

#if defined(USE_CARBON_EVENTS)
/*!
	Carbon event handler
 */
pascal OSStatus XAP_MacApp::HandleCarbonMenus (EventHandlerCallRef nextHandler, 
											EventRef theEvent, void* userData)
{
	OSStatus err = noErr;
	XAP_MacApp *	myApp = (XAP_MacApp *)userData;
	UT_ASSERT (myApp);
	UInt32			eventKind;
	HICommand		cmd;
	
	eventKind = ::GetEventKind (theEvent);
	if (eventKind == kEventCommandProcess) {
		XAP_Menu_Id xapId;
		GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof (HICommand), NULL, &cmd);

		xapId = cmd.commandID;
		
		XAP_MacFrame *theFrame = dynamic_cast<XAP_MacFrame *>(myApp->getLastFocussedFrame ());
		UT_ASSERT (theFrame);
		
		EV_MacMenu *menu = theFrame->getMenu();
		UT_ASSERT (menu);

		menu->onCommand (xapId);
	}
	
	err = CallNextEventHandler (nextHandler, theEvent);
	return err;
}


#else
/*!
	Dispatch the event.
	Classic events only 
*/
void XAP_MacApp::DispatchEvent (const EventRecord & theEvent)
{
	WindowPtr	targetWin;
	short		winLocation;
	XAP_MacFrame *frame = NULL;
	long	menuSelection;
	
	switch (theEvent.what) {
	case keyDown:
		menuSelection = ::MenuEvent (&theEvent);
		if (HiWord (menuSelection != 0)) {
			HandleMenus (menuSelection);
		}
		else {
			// TODO handle key events
		}
		break;
	case mouseDown:
		winLocation = ::FindWindow(theEvent.where, &targetWin);
#if defined(XP_MAC_TARGET_CARBON) && XP_MAC_TARGET_CARBON
		if (::GetWindowKind(targetWin) == XAP_MACFRAME_WINDOW_KIND) {
			frame = (XAP_MacFrame*)GetWRefCon(targetWin);
#else
		if (((WindowRecord *)targetWin)->windowKind == XAP_MACFRAME_WINDOW_KIND) {
			frame = (XAP_MacFrame*)((WindowRecord *)targetWin)->refCon;
#endif
		}
		switch (winLocation) {
		case inMenuBar:
			HandleMenus (::MenuSelect(theEvent.where));
			break;
		case inSysWindow:
#if defined(XP_MAC_TARGET_CARBON) && XP_MAC_TARGET_CARBON
#else
			::SystemClick (&theEvent, targetWin);
#endif
			break;
		case inContent:
			if (frame != NULL) {
				frame->raise ();
			}
			else {
				::BringToFront (targetWin);
			}
			break;
		case inDrag:
			::DragWindow (targetWin, theEvent.where, 
#if defined(XP_MAC_TARGET_CARBON) && XP_MAC_TARGET_CARBON
			NULL			/* valid only for Carbon 1.0 and forward */
#else
			&qd.screenBits.bounds
#endif
			);	/* TODO handle multiple screens */
			break;
		case inGrow:
			{
				long newSize = ::GrowWindow (targetWin, theEvent.where,
#if defined(XP_MAC_TARGET_CARBON) && XP_MAC_TARGET_CARBON
				NULL			/* valid only for Carbon 1.0 and forward */
#else
				&qd.screenBits.bounds
#endif
				);
				::SizeWindow (targetWin, LoWord (newSize), HiWord (newSize), true);
				if (frame != NULL) {
					frame->_macGrow ();
				}
			}
			break;
		case inGoAway:
			if (::TrackGoAway (targetWin, theEvent.where)) {
				if (frame != NULL) {
					frame->close ();
				}
			}
			break;
		case inProxyIcon:
			// TODO handle proxy icons
			break;
		case inZoomIn:
		case inZoomOut:
			if (::TrackBox (targetWin, theEvent.where, winLocation)) {
				if (frame != NULL) {
					
				}
			}			
			break;
		}
		break;
	case updateEvt:
		targetWin = *((WindowPtr*) &theEvent.message);
	#if defined(XP_MAC_TARGET_CARBON) && XP_MAC_TARGET_CARBON
		if (::GetWindowKind(targetWin) == XAP_MACFRAME_WINDOW_KIND) {
			frame = (XAP_MacFrame*)GetWRefCon(targetWin);
	#else
		if (((WindowRecord *)targetWin)->windowKind == XAP_MACFRAME_WINDOW_KIND) {
			frame = (XAP_MacFrame*)((WindowRecord *)targetWin)->refCon;
	#endif
		}
		if (frame != NULL) {
			frame->_macUpdate ();
		}
		break;
	case kHighLevelEvent:
		::AEProcessAppleEvent (&theEvent);
		break;
	} 
}

/* Classic events only */
void XAP_MacApp::HandleMenus (long menuSelection)
{
	short id, item;
	
	id = HiWord (menuSelection);
	item = LoWord (menuSelection);
	
	if (id != 0) {
		XAP_MacFrame *theFrame = dynamic_cast<XAP_MacFrame *>(getLastFocussedFrame ());
		UT_ASSERT (theFrame);
		
		EV_MacMenu *menu = theFrame->getMenu();
		UT_ASSERT (menu);
		
		XAP_Menu_Id xapId = menu->findMenuId (id, item);
		menu->onCommand (xapId);
	}	
	HiliteMenu(0);
}
#endif


/*
	Run the main event loop.
*/
void XAP_MacApp::run ()
{
	unsigned short mask = everyEvent;
	EventRecord theEvent;
	unsigned long delay = ::GetCaretTime();
#if defined(USE_CARBON_EVENTS)
	RunApplicationEventLoop ();
#else
	while (!m_finished) {
		while (::WaitNextEvent(mask, &theEvent, delay, NULL))
		{
			DispatchEvent (theEvent);
		}
	}
#endif
}
