/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
/* 
 * wffppeer.cpp (FontDisplayerPeerObject.cpp)
 *
 * This object is local to the FontDisplayer. One of these objects
 * exist for every FontDisplayer that exists. All calls to the FontDisplayer
 * are routed through this peer object. This takes care of loading and
 * unloading the FontDisplayers as neccessary.
 *
 * dp Suresh <dp@netscape.com>
 */


#include "wffpPeer.h"

#include "fe_proto.h"
#define WF_ONE_MILLESEC 1
#define WF_ONE_MINUTE (60 * 1000 * WF_ONE_MILLESEC)

/* Getting intl string ids */
#define WANT_ENUM_STRING_IDS
#include "allxpstr.h"
#undef WANT_ENUM_STRING_IDS

FontDisplayerPeerObject::
FontDisplayerPeerObject(struct nffp *fp)
	: dlm(NULL), deleted(0), disabled(0), native(-1),
	  streamCount(0), fhList(NULL), unloadTimerId(NULL)
{
	XP_ASSERT(fp);
	fpType = FontDisplayerPeerObject::WF_FP_STATIC;

	fontDisplayer = fp;

	// Although there we aren't expected to free the returned
	// Name and Description, we could unload this dynamic modules
	// and the name and decription could be in their data space.
	// So to prevent losing these, we take a copy.
	displayerName = CopyString(nffp_Name(fontDisplayer, NULL));
	displayerDescription = CopyString(nffp_Description(fontDisplayer, NULL));
	
#if defined(XP_WIN) && !defined(WIN32)
    // Win16 It seems the const char * assignment doesn't work.
	char mimeString[256];
	*mimeString = '\0';
	strcpy(mimeString, nffp_EnumerateMimeTypes(fontDisplayer, NULL));
#else
	const char *mimeString = nffp_EnumerateMimeTypes(fontDisplayer, NULL);
#endif
	if (mimeString && *mimeString )
	{
		mimeList.reconstruct(mimeString);
		registerConverters();
	}
}


FontDisplayerPeerObject::
FontDisplayerPeerObject(const char *dlmName)
	: dlm(dlmName), mimeList(NULL), deleted(0), disabled(0), native(-1),
	  streamCount(0), fhList(NULL), unloadTimerId(NULL)
{
	fpType = FontDisplayerPeerObject::WF_FP_DYNAMIC;

	fontDisplayer = NULL;
	displayerName = NULL;
	displayerDescription = NULL;

	if (dlm.status() < 0)
	{
		deleted = 1;
		return;
	}

#ifdef DEBUG
	// Don't load in purified versions of libraries.
	if(XP_STRSTR(dlmName, "_pure_")) {
		XP_ASSERT(0);
		deleted = 1;
		return;
	}
#endif

	if (load() < 0)
	{
		deleted = 1;
		return;
	}
	displayerName = CopyString(nffp_Name(fontDisplayer, NULL));
	displayerDescription = CopyString(nffp_Description(fontDisplayer,NULL));
	
#if defined(XP_WIN) && !defined(WIN32)
    // Win16 It seems the const char * assignment doesn't work.
	char mimeString[256];
	*mimeString = '\0';
	strcpy(mimeString, nffp_EnumerateMimeTypes(fontDisplayer, NULL));
#else
	const char *mimeString = nffp_EnumerateMimeTypes(fontDisplayer, NULL);
#endif
	if (mimeString && *mimeString )
	{
		mimeList.reconstruct(mimeString);
		registerConverters();
	}
}

FontDisplayerPeerObject::
FontDisplayerPeerObject(FontCatalogFile &fc)
	: dlm(NULL), deleted(0), disabled(0), native(-1),
	  streamCount(0), fhList(NULL), unloadTimerId(NULL)
{
	fpType = FontDisplayerPeerObject::WF_FP_DYNAMIC;
	fontDisplayer = NULL;
	displayerName = NULL;
	displayerDescription = NULL;

	reconstruct(fc);
}


FontDisplayerPeerObject::
~FontDisplayerPeerObject()
{
	finalize();
}

jint FontDisplayerPeerObject::
describe(FontCatalogFile &fc)
{
	char *s;
	
	// Print out the displayer information
	
	if (fpType != WF_FP_DYNAMIC)
	{
		return (0);
	}
	fc.output("displayer = {");
	fc.indentIn();

	s = "dynamic";
	fc.output("type", s);

	s = dlm.describe();
	fc.output("dlm", s);
	if (s)
	{
		XP_FREE(s);
	}

	fc.output("name", displayerName);
	fc.output("description", displayerDescription);

	s = mimeList.describe();
	fc.output("mimeString", s);
	if (s)
	{
		XP_FREE(s);
	}

	fc.output("deleted", deleted);
	fc.output("disabled", disabled);

	fc.output("catalog = {");
	fc.indentIn();
	catalog.describe(fc);
	fc.indentOut();
	fc.output("} // End of catalog");

	fc.indentOut();
	fc.output("} // End of displayer");

	return (0);
}

jint FontDisplayerPeerObject::
reconstruct(FontCatalogFile &fc)
{
	char buf[1024];
	int buflen;

	int over = 0;
	char *variable = NULL;
	char *value = NULL;
	char *ret;

	finalize();
	
	while (!over)
	{
		ret = fc.readline(buf, sizeof(buf));
		if (!ret)
		{
			over = 1;
			continue;
		}
		buflen = strlen((const char *)buf);
		if (buf[buflen-1] == '\n')
		{
			buf[buflen-1] = '\0';
			buflen--;
		}
		if (!strncmp(buf, "#", 1))
		{
			// Ignore comments
			continue;
		}
		wf_scanVariableAndValue(buf, buflen, variable, value);
		if (!wf_strcasecmp(variable, "displayer"))
		{
			continue;
		}
		else if (!wf_strcasecmp(variable, "type"))
		{
			if (!wf_strcasecmp(value, "dynamic"))
			{
				fpType = WF_FP_DYNAMIC;
			}
			else
			{
				deleted = 1;
				over = 1;
				continue;
			}
		}
		else if (!wf_strcasecmp(variable, "dlm"))
		{
			fontDisplayer = NULL;
			dlm.reconstruct(value);
			if (dlm.status() < 0)
			{
				deleted = 1;
				over = 1;
				continue;
			}
		}
		else if (!wf_strcasecmp(variable, "disabled"))
		{
			disabled = atoi(value);
		}
		else if (!wf_strcasecmp(variable, "name"))
		{
			if (displayerName) delete displayerName;
			displayerName = CopyString(value);
		}

		else if (!wf_strcasecmp(variable, "description"))
		{
			if (displayerDescription) delete displayerDescription;
			displayerDescription = CopyString(value);
		}
		else if (!wf_strcasecmp(variable, "mimeString"))
		{
			mimeList.reconstruct(value);
			registerConverters();
		}
		else if (!wf_strcasecmp(variable, "deleted"))
		{
			deleted = atoi(value);
		}
		else if (!wf_strcasecmp(variable, "catalog"))
		{
			catalog.reconstruct(fc);
		}
		else if (!strncmp(variable, "}", 1))
		{
			over = 1;
			continue;
		}
	}

	// sanity check the fpp
	if (dlm.filename() == NULL || dlm.status() <= 0 || fpType != WF_FP_DYNAMIC)
		deleted = 1;
	return (0);
}


jdouble *
FontDisplayerPeerObject::EnumerateSizes(struct nfrc *rc, void *fh)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return NULL;
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return((jdouble *)nffp_EnumerateSizes(fontDisplayer, rc, fh, NULL));
}

struct nffmi * 
FontDisplayerPeerObject::GetMatchInfo(void *fh)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return(nffp_GetMatchInfo(fontDisplayer, fh, NULL));
}

struct nfrf *
FontDisplayerPeerObject::
CreateRenderableFont(struct nfrc *rc, void *fh, jdouble pointsize)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return(nffp_GetRenderableFont(fontDisplayer, rc, fh, pointsize, NULL));
}

void *
FontDisplayerPeerObject::LookupFont(struct nfrc *rc, struct nffmi *fmi, const char *accessor)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return(nffp_LookupFont(fontDisplayer, rc, fmi, accessor, NULL));
}

void *
FontDisplayerPeerObject::CreateFontFromFile(struct nfrc *rc, const char *mimetype,
										   const char *filename, const char *urlOfPage)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return(nffp_CreateFontFromFile(fontDisplayer, rc, mimetype, filename, urlOfPage,
								   NULL));
}

struct nfstrm *
FontDisplayerPeerObject::CreateFontStreamHandler(struct nfrc *rc, const char *urlOfPage)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return((struct nfstrm *)
			nffp_CreateFontStreamHandler(fontDisplayer, rc, urlOfPage, NULL));
}


jint
FontDisplayerPeerObject::ReleaseFontHandle(void *fh)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (-1);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (-1);
	}
	return(nffp_ReleaseFontHandle(fontDisplayer, fh, NULL));
}

//
// Catalogue routines
//

struct nffmi **
FontDisplayerPeerObject::ListFonts(struct nfrc *rc, struct nffmi *fmi)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return((struct nffmi **)
			 nffp_ListFonts(fontDisplayer, rc, fmi, NULL));
}

jdouble *
FontDisplayerPeerObject::ListSizes(struct nfrc *rc, struct nffmi *fmi)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	// Check for call specific preconditions
	if (load() < 0)
	{
		deleted = 1;
		return (NULL);
	}
	return((jdouble *)
			 nffp_ListSizes(fontDisplayer, rc, fmi, NULL));
}

int
FontDisplayerPeerObject::queryCatalog(struct nfrc *rc, struct nffmi *fmi)
{
	int ret = catalog.supportsFmi(rc, fmi);

	if (ret < 0)
	{
		// Catalog may not have been initialized.
		if (!catalog.isInitialized(rc))
		{
			// Now we need to load the dll to initialize the catalog.
			struct nffmi **fmis = ListFonts(rc, NULL);
			catalog.update(rc, fmis);
			
			if (fmis)
			{
				// XXX We need to free the fmis. We may get away
				// XXX doing that if we make catalog.update() not
				// XXX take a copy of the fmi.
				wf_releaseFmis(fmis);
				WF_FREE(fmis);
			}
			ret = catalog.supportsFmi(rc, fmi);
		}
	}
	return (ret);
}


//
// Displayer loading/unloading management routines
//
int
FontDisplayerPeerObject::dlmChanged(const char *dlm_name)
{
	int ret = -1;
	// if this is the dlm that we are associcated with
	if (!strcmp(dlm.filename(), dlm_name))
	{
		if (dlm.isChanged() == 0)
		{
			// Unchanged
			ret = 0;
		}
		else
		{
			// This mean the dlm has either changed or may have been deleted.
			ret = 1;
		}
	}

	return (ret);
}

void
FontDisplayerPeerObject::resync(void)
{
	native = -1;

	finalizeExceptDlmAndDisabled();

	// This might cause a core dump as usually a resync() is done only
	// when we know that the dlm has changed. And if it was loaded in
	// memory and the disk copy was changed whew!
	// Some platforms wont let the copy on disk to change. So we are kind of
	// safe.
	//
	// XXX A better solution would be to not unload it at all but just clear our
	// internal state. Let us see.
	dlm.unload(/*force*/ 1);

	dlm.sync();

	if (dlm.status() < 0)
	{
		deleted = 1;
		return;
	}
	if (load() < 0)
	{
		deleted = 1;
		return;
	}
	displayerName = CopyString(nffp_Name(fontDisplayer, NULL));
	displayerDescription = CopyString(nffp_Description(fontDisplayer,NULL));
	
#if defined(XP_WIN) && !defined(WIN32)
    // Win16 It seems the const char * assignment doesn't work.
	char mimeString[256];
	*mimeString = '\0';
	strcpy(mimeString, nffp_EnumerateMimeTypes(fontDisplayer, NULL));
#else
	const char *mimeString = nffp_EnumerateMimeTypes(fontDisplayer, NULL);
#endif
	if (mimeString && *mimeString )
	{
		mimeList.reconstruct(mimeString);
		registerConverters();
	}
}

//
// mime handling routines
//

int
FontDisplayerPeerObject::countMimetypes()
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (-1);
	}
	return (mimeList.count());
}

int
FontDisplayerPeerObject::isMimetypeEnabled(const char *mimetype)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (0);
	}

	return (mimeList.isEnabled(mimetype));
}

const char *
FontDisplayerPeerObject::getMimetypeFromExtension(const char *ext)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (0);
	}

	return (mimeList.getMimetypeFromExtension(ext));
}


int
FontDisplayerPeerObject::disableMimetype(const char *mimetype)
{
	// Check for generic preconditions
	if (deleted)
	{
		return (-1);
	}

	return (mimeList.setEnabledStatus(mimetype, 0));
}

int
FontDisplayerPeerObject::enableMimetype(const char *mimetype)
{
	// Check for generic preconditions
	if (deleted)
	{
		return (-1);
	}

	return (mimeList.setEnabledStatus(mimetype, 1));
}

//
// Additional routines
//

const char *
FontDisplayerPeerObject::name(void)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (NULL);
	}

	return (displayerName);
}

int
FontDisplayerPeerObject::isNative(void)
{
	if (native < 0)
	{
		native = 0;	// not native
		if (displayerName)
		{
			native = (strcmp(displayerName, NF_NATIVE_FONT_DISPLAYER) == 0);
		}
	}
	// Check if this is the native font displayer
	return (native);
}

int
FontDisplayerPeerObject::isDeleted(void)
{
	return (deleted);
}

int
FontDisplayerPeerObject::isLoaded(void)
{
	int ret = 0;

	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (ret);
	}

	if (fontDisplayer || fpType == FontDisplayerPeerObject::WF_FP_STATIC)
	{
		ret = 1;
	}
	return (ret);
}


void
FontDisplayerPeerObject::StreamCreated(struct nfstrm *strm)
{
	streamCount++;
}

void
FontDisplayerPeerObject::StreamDone(struct nfstrm *strm)
{
	streamCount--;

	decideToUnload();
}

void
FontDisplayerPeerObject::FontHandleCreated(void *fh)
{
	fhList.add(fh);
}

void
FontDisplayerPeerObject::FontHandleDone(void *fh)
{
	if (fhList.remove(fh) == wfList::SUCCESS)
	{
		// If there was no other copy of the fh in our fhlist
		// then this was the last fh and can be released.
		if (fhList.isExist(fh) != wfList::SUCCESS)
		{
			ReleaseFontHandle(fh);
			
			if (fpType == FontDisplayerPeerObject::WF_FP_DYNAMIC)
			{
				// Static displayers need not be unloaded.
				decideToUnload();
			}
		}
	}
}


int
FontDisplayerPeerObject::disableDisplayer(void)
{
	if (deleted)
	{
		return (-1);
	}

	disabled = 1;
	return (0);
}

int
FontDisplayerPeerObject::enableDisplayer(void)
{
	if (deleted)
	{
		return (-1);
	}

	disabled = 0;
	return (0);
}

int
FontDisplayerPeerObject::isDisplayerEnabled(void)
{
	if (deleted)
	{
		return (0);
	}
	return ((disabled ? 0 : 1));
}


int
FontDisplayerPeerObject::registerConverters(void)
{
	if (deleted)
	{
		return (-1);
	}

	if (!mimeList.isEmpty())
	{
		// Foreach mime
		struct wfListElement *tmp = mimeList.head;
		for (; tmp; tmp = tmp->next)
		{
			struct mime_store *ele = (struct mime_store *) tmp->item;
			NET_cdataCommit(ele->mimetype, ele->extensions);
		}
	}
	
	return (0);
}


char *
FontDisplayerPeerObject::aboutData(void)
{
	char *aboutData = NULL;
	int aboutDataLen = 0;
	int aboutDataMaxLen = 0;
#ifndef NO_HTML_DIALOGS_CHANGE
#define WF_ABOUT_DATA_ALLOCATION_STEP 64
#define WF_ACCUMULATE(str) wf_addToString(&aboutData, &aboutDataLen, &aboutDataMaxLen, str);

	// Dont show deleted font displayers.
	if (isDeleted())
	{
		return (NULL);
	}


	// Displayer data
    char *fmtstring = NULL;
    char *buf;
    if (fpType == FontDisplayerPeerObject::WF_FP_STATIC)
    {
        fmtstring = XP_GetString(WF_MSG_ABOUT_DISPLAYER_STATIC);
        buf = PR_smprintf(fmtstring,
			(displayerName ? displayerName : ""),
			(displayerDescription ? displayerDescription : ""));
    }
    else
    {
        fmtstring = XP_GetString(WF_MSG_ABOUT_DISPLAYER_DYNAMIC);
        buf = PR_smprintf(fmtstring,
			(displayerName ? displayerName : ""),
			(displayerDescription ? displayerDescription : ""),
			dlm.filename());
    }
    if (buf)
    {
        WF_ACCUMULATE(buf);
        XP_FREE(buf);
        buf = NULL;
    }
	if (!mimeList.isEmpty())
	{
		// Foreach mime
		struct wfListElement *tmp = mimeList.head;
		for (; tmp; tmp = tmp->next)
		{
			struct mime_store *ele = (struct mime_store *) tmp->item;
			buf = PR_smprintf(XP_GetString(WF_MSG_ABOUT_DISPLAYER_MIME),
				ele->mimetype,
				(displayerName ? displayerName : ""),
				(ele->isEnabled ? "checked" : ""), /* NO I18N */
				ele->mimetype, ele->description,
				ele->extensions);
			if (buf)
			{
				WF_ACCUMULATE(buf);
				XP_FREE(buf);
				buf = NULL;
			}
		}
	}
	WF_ACCUMULATE(XP_GetString(WF_MSG_ABOUT_DISPLAYER_END));
#endif /* NO_HTML_DIALOGS_CHANGE */
	return (aboutData);
}
	
//
// Private method implementations
//

int
FontDisplayerPeerObject::load(void)
{
	// Check for generic preconditions
	if (deleted || disabled)
	{
		return (0);
	}

	if (fontDisplayer || fpType == FontDisplayerPeerObject::WF_FP_STATIC)
	{
		return (0);
	}

	dlm.load();
	if (dlm.status() < 0)
	{
		return (-1);
	}

	fontDisplayer = dlm.createDisplayerObject(WF_fbp);
	if (!fontDisplayer)
	{
		WF_TRACEMSG(("NF: dlm (%s) Couldn't create fontdisplayer object. Skipping dlm.",
				  dlm.filename()));
		dlm.unload();
		return (-1);
	}
	return (0);
}

extern "C" void wf_unloadTimer(void *closure)
{
	FontDisplayerPeerObject *fpp = (FontDisplayerPeerObject *)closure;
	if (fpp->unloadTimerId)
	{
		// One last sanity check
		if (fpp->streamCount == 0 && fpp->fhList.isEmpty())
		{
			fpp->unload();
		}
		fpp->unloadTimerId = 0;
	}
}

void
FontDisplayerPeerObject::decideToUnload(void)
{
	if (streamCount == 0 && fhList.isEmpty())
	{
		// This displayer can be unloaded.
		// I really want to do something like unloading the displayer
		// after say 1 min. because say the navigator moves from one page
		// to another and both had use for this displayer, then this displayer
		// will get loaded twice and unloaded once causing thrashing.
		//
		// The problem in implementing this is how to we ensure we get
		// back control after this many seconds. We should use the
		// FE Timer callback.
		
		// Clear any previous timers
		if (unloadTimerId)
		{
			FE_ClearTimeout(unloadTimerId);
		}

		// Set the new timer
		unloadTimerId = FE_SetTimeout(wf_unloadTimer, this, WF_ONE_MINUTE);
	}
	else
	{
		// Unload should not happen
		if (unloadTimerId)
		{
			FE_ClearTimeout(unloadTimerId);
		}
	}
}

int
FontDisplayerPeerObject::unload(void)
{
	if (fpType == FontDisplayerPeerObject::WF_FP_STATIC)
	{
		// Static displayer need not be unloaded.
		return (0);
	}
	if (!fontDisplayer)
	{
		// We were never loaded.
		return (-1);
	}
	nffp_release(fontDisplayer, NULL);
	fontDisplayer = NULL;
	return (dlm.unload());
}

int FontDisplayerPeerObject::
finalizeExceptDlmAndDisabled()
{
	// Release memory
	if (displayerName)
	{
		delete displayerName;
		displayerName = NULL;
	}
	if (displayerDescription)
	{
		delete displayerDescription;
		displayerDescription = NULL;
	}

	// Clear native flag
	native = -1;
	// Clear deleted flag
	deleted = 0;
	
	// Clean mimelist
	mimeList.finalize();

	// Clean catalog
	catalog.finalize();

	return (0);
}

int FontDisplayerPeerObject::
finalize()
{
	finalizeExceptDlmAndDisabled();

	// Clean dlm
	unload();
	dlm.finalize();
	
	// Clear disabled flag
	disabled = 0;

	return (0);
}

