/* AbiSource Application Framework
 * Copyright (C) 1998,1999 AbiSource, Inc.
 * 
 * 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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

#include "ut_types.h"
#include "ut_assert.h"
#include "ut_string.h"
#include "ut_debugmsg.h"
#include "ev_EditMethod.h"
#include "ev_Menu_Actions.h"
#include "ev_Toolbar_Actions.h"
#include "xap_App.h"
#include "xap_Args.h"
#include "gr_Image.h"
#include "xap_Frame.h"
#include "xap_EditMethods.h"
#include "xap_Menu_ActionSet.h"
#include "xap_Toolbar_ActionSet.h"
#include "xap_LoadBindings.h"
#include "xap_Dictionary.h"
#include "xap_Prefs.h"
#include "xap_EncodingManager.h"
#include "xap_Menu_Layouts.h"
#include "xap_Toolbar_Layouts.h"
#include "ut_rand.h"

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

XAP_App * XAP_App::m_pApp = NULL;
bool XAP_App::s_bShowDisplay = true;

XAP_App * XAP_App::getApp() {return m_pApp;}

XAP_App::XAP_App(XAP_Args * pArgs, const char * szAppName)
	: m_pArgs(pArgs),
	  m_szAppName(szAppName),
	  m_szAbiSuiteLibDir(NULL),
	  m_pEMC(NULL),
	  m_pBindingSet(NULL),
	  m_pMenuActionSet(NULL),
	  m_pToolbarActionSet(NULL),
	  m_pDict(NULL),
	  m_prefs(NULL),
	  m_hashClones(5),
	  m_lastFocussedFrame(NULL),
	  m_pMenuFactory(NULL),
	  m_pToolbarFactory(NULL),
	  m_bAllowCustomizing(true)
{
	UT_ASSERT(szAppName && *szAppName);
	m_pApp = this;
	UT_DEBUGMSG(("SEVIOR: Building menus and toolbars \n"));
	m_pMenuFactory = new XAP_Menu_Factory(this);
	m_pToolbarFactory = new XAP_Toolbar_Factory(this);
	clearIdTable();
}

XAP_App::~XAP_App()
{
	// HACK: for now, this works from XAP code
	// TODO: where should this really go?
	if (m_pDict)
		m_pDict->save();

	// run thru and destroy all frames on our window list.
	UT_VECTOR_PURGEALL(XAP_Frame *, m_vecFrames);

	FREEP(m_szAbiSuiteLibDir);
	DELETEP(m_pEMC);
	DELETEP(m_pBindingSet);
	DELETEP(m_pMenuActionSet);
	DELETEP(m_pToolbarActionSet);
	DELETEP(m_pDict);
	DELETEP(m_prefs);
	DELETEP(m_pMenuFactory);
	DELETEP(m_pToolbarFactory);

	// Delete the instance of the Encoding Manager.
	XAP_EncodingManager::get_instance()->Delete_instance();
}

const char* XAP_App::getBuildId ()
{
  return s_szBuild_ID;
}

const char* XAP_App::getBuildVersion ()
{
  return s_szBuild_Version;
}

const char* XAP_App::getBuildOptions ()
{
  return s_szBuild_Options;
}

const char* XAP_App::getBuildTarget ()
{
  return s_szBuild_Target;
}

const char* XAP_App::getBuildCompileTime ()
{
  return s_szBuild_CompileTime;
}

const char* XAP_App::getBuildCompileDate ()
{
  return s_szBuild_ID;
}

/*! this function is silly */
const XAP_EncodingManager* XAP_App::getEncodingManager() const 
{ 
    return XAP_EncodingManager::get_instance();
};

EV_Menu_ActionSet *XAP_App::getMenuActionSet()
{
	return m_pMenuActionSet;
}

EV_Toolbar_ActionSet *XAP_App::getToolbarActionSet()
{
	return m_pToolbarActionSet;
}

bool XAP_App::initialize()
{
	// create application-wide resources that
	// are shared by everything.

#if 0 
	m_pEMC = AP_GetEditMethods();
	UT_ASSERT(m_pEMC);

	m_pBindingSet = new AP_BindingSet(m_pEMC);
	UT_ASSERT(m_pBindingSet);
	
	m_pMenuActionSet = AP_CreateMenuActionSet();
	UT_ASSERT(m_pMenuActionSet);

	m_pToolbarActionSet = AP_CreateToolbarActionSet();
	UT_ASSERT(m_pToolbarActionSet);
#endif

	// HACK: for now, this works from XAP code
	// TODO: where should this really go?
	char * szPathname = UT_catPathname(getUserPrivateDirectory(),"custom.dic");
	UT_ASSERT(szPathname);
	m_pDict = new XAP_Dictionary(szPathname);
	FREEP(szPathname);
	UT_ASSERT(m_pDict);
	m_pDict->load();
	clearIdTable();
//
// Reload toolbar configuration from prefs
//
	bool bAllowCustom = true;
	getPrefsValueBool(XAP_PREF_KEY_AllowCustomToolbars, &bAllowCustom);
	if(bAllowCustom)
	{
		m_pToolbarFactory->restoreToolbarsFromCurrentScheme();
		setToolbarsCustomizable(true);
	}
	else
	{
		setToolbarsCustomizable(false);
	}
	// TODO use m_pArgs->{argc,argv} to process any command-line
	// TODO options that we need.
	//
	// Need to initialize the random number generator. 
	//
	UT_uint32 t = (UT_uint32) time( NULL);
	UT_srandom(t);
	return true;
}

void XAP_App::setToolbarsCustomizable(bool b)
{
	if(m_bAllowCustomizing && !b)
	{
//
// Set all the frames to default toolbars
//
		m_bAllowCustomizing = b;
		m_pToolbarFactory->resetAllToolbarsToDefault();
		UT_uint32 count = m_vecFrames.getItemCount();
		UT_Vector vClones;
		UT_uint32 i = 0;
		for(i=0; i< count; i++)
		{
			XAP_Frame * pFrame = (XAP_Frame *) m_vecFrames.getNthItem(i);
			if(pFrame->getViewNumber() > 0)
			{
				getClones(&vClones,pFrame);
				UT_uint32 j=0;
				for(j=0; j < vClones.getItemCount(); j++)
				{
					XAP_Frame * f = (XAP_Frame *) vClones.getNthItem(j);
					f->rebuildAllToolbars();
				}
			}
			else
			{
				pFrame->rebuildAllToolbars();
			}
		}
	}
	m_bAllowCustomizing = b;
}

const char * XAP_App::getApplicationTitleForTitleBar() const
{
	static char _title[512];

	// return a string that the platform-specific code
	// can copy to the title bar of a window.

	//sprintf(_title, "%s (www.abisource.com)", m_szAppName);
	sprintf(_title, "%s", m_szAppName);

	return _title;
}

const char * XAP_App::getApplicationName() const
{
	// return a string that the platform-specific code
	// can use as a class name for various window-manager-like
	// operations.
	return m_szAppName;
}

EV_EditMethodContainer * XAP_App::getEditMethodContainer() const
{
	return m_pEMC;
}

EV_EditBindingMap * XAP_App::getBindingMap(const char * szName)
{
	UT_ASSERT(m_pBindingSet);
	return m_pBindingSet->getMap(szName);
}

const EV_Menu_ActionSet * XAP_App::getMenuActionSet() const
{
	return m_pMenuActionSet;
}

const EV_Toolbar_ActionSet * XAP_App::getToolbarActionSet() const
{
	return m_pToolbarActionSet;
}

bool XAP_App::rememberFrame(XAP_Frame * pFrame, XAP_Frame * pCloneOf)
{
	UT_ASSERT(pFrame);

	// add this frame to our window list
	m_vecFrames.addItem(pFrame);

	if(! m_lastFocussedFrame)
	    rememberFocussedFrame(pFrame);

	// TODO: error-check the following for mem failures
	if (pCloneOf)
	{
		// locate vector of this frame's clones
		const void * pEntry = m_hashClones.pick(pCloneOf->getViewKey());
		UT_Vector * pvClones = NULL;

		if (pEntry)
		{
			// hash table entry already exists
			pvClones = (UT_Vector *) pEntry;

			if (!pvClones)
			{
				// nothing there, so create a new one
				pvClones = new UT_Vector();
				UT_ASSERT(pvClones);

				pvClones->addItem(pCloneOf);

				// reuse this slot
				m_hashClones.set(pCloneOf->getViewKey(), 
						 (void *)pvClones);
			}
		}
		else
		{
			// create a new one
			pvClones = new UT_Vector();
			UT_ASSERT(pvClones);

			pvClones->addItem(pCloneOf);

			// add it to the hash table
			m_hashClones.insert(pCloneOf->getViewKey(), (void *)pvClones);
		}

		pvClones->addItem(pFrame);

		// notify all clones of their new view numbers
		for (UT_uint32 j=0; j<pvClones->getItemCount(); j++)
		{
			XAP_Frame * f = (XAP_Frame *) pvClones->getNthItem(j);
			UT_ASSERT(f);

			f->setViewNumber(j+1);

			if (f != pFrame)
				f->updateTitle();
		}
	}
	
	// TODO do something here...
	return true;
}

bool XAP_App::forgetFrame(XAP_Frame * pFrame)
{
	UT_ASSERT(pFrame);

	// If this frame is the currently focussed frame write in NULL
	// until another frame appears

	if(pFrame == m_lastFocussedFrame )
	{
		m_lastFocussedFrame = (XAP_Frame *) NULL;
	}

	if (pFrame->getViewNumber() > 0)
	{
		// locate vector of this frame's clones
		const void * pEntry = m_hashClones.pick(pFrame->getViewKey());
		UT_ASSERT(pEntry);

		if (pEntry)
		{
			UT_Vector * pvClones = (UT_Vector *) pEntry;
			UT_ASSERT(pvClones);

			// remove this frame from the vector
			UT_sint32 i = pvClones->findItem(pFrame);
			UT_ASSERT(i >= 0);

			if (i >= 0)
			{
				pvClones->deleteNthItem(i);
			}

			// see how many clones are left
			UT_uint32 count = pvClones->getItemCount();
			UT_ASSERT(count > 0);
			XAP_Frame * f = NULL;

			if (count == 1)
			{
				// remaining clone is now a singleton
				f = (XAP_Frame *) pvClones->getNthItem(count-1);
				UT_ASSERT(f);

				f->setViewNumber(0);
				f->updateTitle();

				// remove this entry from hashtable
				m_hashClones.remove(f->getViewKey(), 
						    NULL);
				delete pvClones;
			}
			else
			{
				// notify remaining clones of their new view numbers
				for (UT_uint32 j=0; j<count; j++)
				{
					f = (XAP_Frame *) pvClones->getNthItem(j);
					UT_ASSERT(f);

					f->setViewNumber(j+1);
					f->updateTitle();
				}
			}
		}
	}

	// remove this frame from our window list
	UT_sint32 ndx = m_vecFrames.findItem(pFrame);
	UT_ASSERT(ndx >= 0);

	if (ndx >= 0)
	{
		m_vecFrames.deleteNthItem(ndx);
	}

	notifyModelessDlgsCloseFrame(pFrame);

	// TODO do something here...

	return true;
}

bool XAP_App::forgetClones(XAP_Frame * pFrame)
{
	UT_ASSERT(pFrame);

	if (pFrame->getViewNumber() == 0)
	{
		return forgetFrame(pFrame);
	}

	UT_Vector vClones;
	getClones(&vClones, pFrame);
	
	for (UT_uint32 i = 0; i < vClones.getItemCount(); i++)
	{
		XAP_Frame * f = (XAP_Frame *) vClones.getNthItem(i);
		forgetFrame(f);
	}

	return true;
}

bool XAP_App::getClones(UT_Vector *pvClonesCopy, XAP_Frame * pFrame)
{
	UT_ASSERT(pvClonesCopy);
	UT_ASSERT(pFrame);
	UT_ASSERT(pFrame->getViewNumber() > 0);

	// locate vector of this frame's clones
	const void * pEntry = m_hashClones.pick(pFrame->getViewKey());
	UT_ASSERT(pEntry);

	UT_Vector * pvClones = (UT_Vector *) pEntry;
	UT_ASSERT(pvClones);

	return pvClonesCopy->copy(pvClones);
}

bool XAP_App::updateClones(XAP_Frame * pFrame)
{
	UT_ASSERT(pFrame);
	UT_ASSERT(pFrame->getViewNumber() > 0);

	// locate vector of this frame's clones
	const void * pEntry = m_hashClones.pick(pFrame->getViewKey());
	UT_ASSERT(pEntry);

	if (pEntry)
	{
		UT_Vector * pvClones = (UT_Vector *) pEntry;
		UT_ASSERT(pvClones);

		UT_uint32 count = pvClones->getItemCount();
		UT_ASSERT(count > 0);
		XAP_Frame * f = NULL;

		for (UT_uint32 j=0; j<count; j++)
		{
			f = (XAP_Frame *) pvClones->getNthItem(j);
			UT_ASSERT(f);

			f->updateTitle();
		}
	}

	return true;
}

UT_uint32 XAP_App::getFrameCount() const
{
	return m_vecFrames.getItemCount();
}

XAP_Frame * XAP_App::getFrame(UT_uint32 ndx) const
{
	XAP_Frame * pFrame = NULL;
	
	if (ndx < m_vecFrames.getItemCount())
	{
		pFrame = (XAP_Frame *) m_vecFrames.getNthItem(ndx);
		UT_ASSERT(pFrame);
	}
	return pFrame;
}
	
UT_sint32 XAP_App::findFrame(XAP_Frame * pFrame)
{
	return m_vecFrames.findItem(pFrame);
}
	
UT_sint32 XAP_App::findFrame(const char * szFilename)
{
	if (!szFilename || !*szFilename)
		return -1;

	for (UT_uint32 i=0; i<getFrameCount(); i++)
	{
		XAP_Frame * f = getFrame(i);
		UT_ASSERT(f);
		const char * s = f->getFilename();

		if (s && *s && (0 == UT_stricmp(szFilename, s)))
		{
			return (UT_sint32) i;
		}
	}

	return -1;
}

void XAP_App::_setAbiSuiteLibDir(const char * sz)
{
	FREEP(m_szAbiSuiteLibDir);
	UT_cloneString((char *&)m_szAbiSuiteLibDir,sz);
}

const char * XAP_App::getAbiSuiteLibDir() const
{
	return m_szAbiSuiteLibDir;
}

bool XAP_App::addWordToDict(const UT_UCSChar * pWord, UT_uint32 len)
{
	if (!m_pDict)
		return false;

	return m_pDict->addWord(pWord, len);
}

bool XAP_App::isWordInDict(const UT_UCSChar * pWord, UT_uint32 len) const
{
	if (!m_pDict)
		return false;

	return m_pDict->isWord(pWord, len);
}

/*!
 * Look up the custom dictionary for suggested words
 */
void    XAP_App::suggestWord(UT_Vector * pVecSuggestions, const UT_UCSChar * pWord, UT_uint32 lenWord)
{
  if(m_pDict)
  {
       m_pDict->suggestWord(pVecSuggestions, pWord, lenWord);
  }
}

XAP_Prefs * XAP_App::getPrefs() const
{
	return m_prefs;
}

bool XAP_App::getPrefsValue(const XML_Char * szKey, const XML_Char ** pszValue) const
{
	if (!m_prefs)
		return false;

	return m_prefs->getPrefsValue(szKey,pszValue);
}

bool XAP_App::getPrefsValue(const UT_String &stKey, UT_String &stValue) const
{
	if (!m_prefs)
		return false;

	return m_prefs->getPrefsValue(stKey, stValue);
}

bool XAP_App::getPrefsValueBool(const XML_Char * szKey, bool * pbValue) const
{
	if (!m_prefs)
		return false;

	return m_prefs->getPrefsValueBool(szKey,pbValue);
}

void XAP_App::rememberFocussedFrame( void * pJustFocussedFrame)
{
	m_lastFocussedFrame = (XAP_Frame *) pJustFocussedFrame;

	UT_sint32 i = safefindFrame( m_lastFocussedFrame);
	if(i < 0 ) 
	{   
		m_lastFocussedFrame = (XAP_Frame *) NULL;
	}
	notifyModelessDlgsOfActiveFrame(m_lastFocussedFrame);
}

UT_sint32 XAP_App::safefindFrame( XAP_Frame * f)
{
	long ff = reinterpret_cast<long>(f);
	UT_sint32 num_frames = m_vecFrames.getItemCount();
	UT_sint32 i;
	for( i = 0; i< num_frames; i++)
	{
		long lf = reinterpret_cast<long>( m_vecFrames.getNthItem(i));
		if( lf == ff) break;
	}
	if( i == num_frames ) i = -1;
	return i;
}

void XAP_App::clearLastFocussedFrame()
{
	m_lastFocussedFrame = (XAP_Frame *) NULL;
}

XAP_Frame* XAP_App::getLastFocussedFrame() 
{
	if(m_lastFocussedFrame == (XAP_Frame *) NULL)
		return (XAP_Frame *) NULL;
	UT_sint32 i = safefindFrame(m_lastFocussedFrame);
	if( i>= 0)
		return m_lastFocussedFrame;
	return (XAP_Frame *) NULL;
}

XAP_Frame * XAP_App::findValidFrame()
{
        XAP_Frame * validFrame =  getFrame(0);
        return validFrame;
}

void XAP_App::clearIdTable()
{
	for(UT_sint32 i =0; i <= NUM_MODELESSID; i++)
	{
		m_IdTable[i].id =  -1;
		m_IdTable[i].pDialog = (XAP_Dialog_Modeless *) NULL;
	}
}

void XAP_App::rememberModelessId(UT_sint32 id, XAP_Dialog_Modeless * pDialog)
{
	// find a free slot in the m_IdTable
 
	UT_sint32 i;
	for(i=0; (i<= NUM_MODELESSID) && (m_IdTable[i].id !=  -1); i++);
	UT_ASSERT( i <= NUM_MODELESSID );
	UT_ASSERT( m_IdTable[i].id == -1 );
	UT_ASSERT( pDialog);
	m_IdTable[i].id =  id;
	m_IdTable[i].pDialog =  pDialog;
}

void XAP_App::forgetModelessId( UT_sint32 id )
{
	// remove the id, pDialog pair from the m_IdTable

	UT_sint32 i;
	for(i=0; i <= NUM_MODELESSID && m_IdTable[i].id != id; i++) ;
	UT_ASSERT( i <= NUM_MODELESSID );
	UT_ASSERT( m_IdTable[i].id == id );
	m_IdTable[i].id =  -1;
	m_IdTable[i].pDialog = (XAP_Dialog_Modeless *) NULL;
}

bool XAP_App::isModelessRunning(UT_sint32 id)
{
	// returns true if the modeless dialog given by id is running
	
	UT_sint32 i;
	for(i=0; i <= NUM_MODELESSID && m_IdTable[i].id != id; i++) ;
	if( i> NUM_MODELESSID)
	{
		return false;
	}
	UT_ASSERT( m_IdTable[i].id == id );
	return true;
}
        
XAP_Dialog_Modeless * XAP_App::getModelessDialog( UT_sint32 i)
{
	// Retrieve pDialog from the table based on its location in the table
	return m_IdTable[i].pDialog;
}

void XAP_App::closeModelessDlgs()
{
	// Delete all the open modeless dialogs

	for(UT_sint32 i=0; i <= NUM_MODELESSID; i++)
	{
		if(getModelessDialog(i) != (XAP_Dialog_Modeless *) NULL)
		{
			getModelessDialog(i)->destroy();
		}
	}
}


void XAP_App::notifyModelessDlgsOfActiveFrame(XAP_Frame *p_Frame)
{
	for(UT_sint32 i=0; i <= NUM_MODELESSID; i++)
	{
		if(getModelessDialog(i) != (XAP_Dialog_Modeless *) NULL)
		{
			getModelessDialog(i)->setActiveFrame(p_Frame);
		}
	}
}

void XAP_App::notifyModelessDlgsCloseFrame(XAP_Frame *p_Frame)
{
	for(UT_sint32 i=0; i <= NUM_MODELESSID; i++)
	{
		if(getModelessDialog(i) != (XAP_Dialog_Modeless *) NULL)
		{
			getModelessDialog(i)->notifyCloseFrame(p_Frame);
		}
	}
}

/* Window Geometry Preferences */
bool XAP_App::setGeometry(UT_sint32 x, UT_sint32 y, UT_uint32 width, UT_uint32 height , UT_uint32 flags) 
{
	XAP_Prefs *prefs = getPrefs();
	return prefs->setGeometry(x, y, width, height, flags);
}

bool XAP_App::getGeometry(UT_sint32 *x, UT_sint32 *y, UT_uint32 *width, UT_uint32 *height, UT_uint32 *flags) 
{
	XAP_Prefs *prefs = getPrefs();
	return prefs->getGeometry(x, y, width, height, flags);
}

void XAP_App::parseAndSetGeometry(const char *string) {
	UT_uint32 nw, nh, nflags;
    UT_sint32 nx, ny;
    char *next;

	nw = nh = nflags = 0;
	nx = ny = 0;

    next = (char *)string;
    if (*next != '+' && *next != '-') {
        nw = strtoul(next, &next, 10);
        if(*next == 'x' || *next == 'X') {
            nh = strtoul(++next, &next, 10);
            nflags |= PREF_FLAG_GEOMETRY_SIZE;
        }
    }
    if (*next == '+' || *next == '-') {
        nx = strtoul(next, &next, 10);
        if(*next == '+' || *next == '-') {
            ny = strtoul(next, &next, 10);
            nflags |= PREF_FLAG_GEOMETRY_POS;
        }
    }

	//Don't update the geometry from the file
	if(nflags) {
		nflags |= PREF_FLAG_GEOMETRY_NOUPDATE;
		setGeometry(nx, ny, nw, nh, nflags);
	}
} 

void XAP_App::_printUsage()
{
	// just print to stdout, not stderr
	printf("\nUsage: %s [option]... [file]...\n\n", m_pArgs->m_argv[0]);
	printf("  -to               The target format of the file\n");
        printf("                    (abw, zabw, rtf, txt, utf8, html, latex)\n");
	printf("  -verbose          The verbosity level (0, 1, 2)\n");
	printf("  -show             If you really want to start the GUI\n");
        printf("                    (even if you use the -to option)\n");
#ifdef DEBUG
	printf("  -dumpstrings      dump strings strings to file\n");
#endif
	printf("  -geometry geom    set initial frame geometry\n");
	printf("  -lib dir          use dir for application components\n");
	printf("  -nosplash         do not show splash screen\n");

	printf("\n");
}


bool XAP_App::setDisplayStatus(bool b)
{
    s_bShowDisplay = b;
    return true;
}

bool XAP_App::getDisplayStatus(void) const
{
	return s_bShowDisplay;
}








