/* -*- 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.
 */
/* 
 * f.cpp (FontObject.cpp)
 *
 * C++ implementation of the (f) FontObject
 *
 * dp Suresh <dp@netscape.com>
 */


#include "f.h"
#include "nf.h"


FontObject::
FontObject(struct nff *f, struct nfrc *irc, const char *url)
		   : self(f), inGC(0), wfList(free_fh_store), iswebfont(0), state(NF_FONT_COMPLETE), shared(1)
{
	m_rcMajorType = nfrc_GetMajorType(irc, NULL);
	m_rcMinorType = nfrc_GetMinorType(irc, NULL);
	urlOfFont = CopyString(url);
	if (urlOfFont && *urlOfFont)
	{
		// Webfont
		iswebfont = 1;
		state = NF_FONT_INCOMPLETE;
	}
}

FontObject::
~FontObject()
{
	if (urlOfFont) delete (char *)urlOfFont;
}


jdouble * FontObject::
EnumerateSizes(struct nfrc *rc)
{
	wfListElement *tmp = head;
	jdouble *ret = NULL;
	int max_ret_len = 0;

    for (; tmp; tmp = tmp->next)
	  {
		struct fh_store *ele = (struct fh_store *)tmp->item;
		
		computeSizes(rc, ele);
		jdouble *sizes = ele->sizesList.getSizes();
		if (sizes)
		{
			MergeSizes(ret, max_ret_len, sizes);
		}
	}
	return (ret);
}

/*ARGSUSED*/
struct nfrf * FontObject::
GetRenderableFont(struct nfrc *rc, jdouble pointsize)
{
	struct nfrf * rf = NULL;
	wfListElement *tmp = head;
	int onlyOne = (head == tail);

	if (!onlyOne)
	{
		WF_TRACEMSG(("NF: More than one displayer handling font 0x%x.", self));
	}

	// Sanity check to make sure that the rc is of the same type
	if (!nfrc_IsEquivalent(rc, m_rcMajorType, m_rcMinorType, NULL))
	{
		// Invalid rc.
		return NULL;
	}

    for (; tmp; tmp = tmp->next)
	{
		struct fh_store *ele = (struct fh_store *)tmp->item;

		// Get any cached rf.
		rf = ele->sizesList.getRf(pointsize);
		if (rf)
		{
			// Increment the refcount of the cached rf.
			WF_TRACEMSG(("NF: Found rf %x (f = 0x%x, pointsize = %6.2f) in cache.",
				rf, self, pointsize));
			nfrf_addRef(rf, NULL);
			break;
		}

		// Try to get a new rf.
		// The right way to do this is to check with the list of enumerated
		// sizes to see if the displayer does support this size. We will
		// however, ask the displayer anyway if there was only one
		// displayer that handled this font.
		if (!onlyOne)
		{
			computeSizes(rc, ele);
			if (!ele->sizesList.supportsSize(pointsize))
			{
				// point size not supported by this displayer. Try next....
				continue;
			}
		}
		rf = ele->fppeer->CreateRenderableFont(rc, ele->fh, pointsize);
		if (!rf)
		{
			// That displayer lied about the sizes it can handle.
			// Remove the size from the sizes list.
			ele->sizesList.removeSize(pointsize);
		}
		else
		{
			WF_TRACEMSG(("NF: Created new rf %x (f = 0x%x, pointsize = %6.2f) from displayer %s.",
				rf, self, pointsize, ele->fppeer->name()));
			
			// Cache this rf.
			int ret = ele->sizesList.addRf(rf);
			//
			// if ret < 0, then we ran out of memory
			// This is very bad because we are now having the
			// fh refcount the nff. And the fh would need to know
			// exactly how many rf's were created with it. If an
			// rf was created but not added to the sizesList, then
			// the fh will be deleted one too soon causing access of
			// deleted object when the final RfDone() happens.
			break;
		}
	}
	return(rf);
}
struct nffmi *
FontObject::GetMatchInfo(struct nfrc *rc, jdouble pointsize)
{
	struct nffmi *fmi = NULL;
	wfListElement *tmp = head;

    for (; tmp; tmp = tmp->next)
	  {
		struct fh_store *ele = (struct fh_store *)tmp->item;
		computeSizes(rc, ele);

		if (ele->sizesList.supportsSize(pointsize))
		  {
			// Get the fh corresponding to this fontdisplayer
			fmi = ele->fppeer->GetMatchInfo(ele->fh);

			if (!fmi)
			  {
				// That displayer lied about the sizes it can handle.
				// Remove the size from the sizes list.
				ele->sizesList.removeSize(pointsize);
			  }
			else
			  {
				break;
			  }
		  }
	  }
	return (fmi);
}

jint
FontObject::GetRcMajorType()
{
	return (m_rcMajorType);
}
 
jint
FontObject::GetRcMinorType()
{
	return (m_rcMinorType);
}


int
FontObject::isShared(void)
{
	return (shared);
}

int
FontObject::setShared(int sharedState)
{
	shared = sharedState;
	return (0);
}


//
// FontBroker specific
//

int FontObject::
addFontHandle(FontDisplayerPeerObject *fppeer, void *fh)
{
	struct fh_store *ele = new fh_store;
	if (!ele) {
		// No memory.
		return (-1);
	}
	ele->fppeer = fppeer;
	ele->fh = fh;
	add((void *)ele);

	// Tell wffpPeer that we created a fh
	fppeer->FontHandleCreated(fh);

	//
	// We will have the fonthandle refcount the Font too.
	// A font should not be deleted until all its fonthandles
	// are deleted.
	//
	nff_addRef(self, NULL);

	// If the displayer is a non-native displayer
	// (The correct condition is if this is webfont)
	// then disable shared
	if (!fppeer->isNative())
	{
		setShared(0);
	}
	
	return (0);
}


int FontObject::
isRfExist(struct nfrf *rf)
{
	wfListElement *tmp = head;
	int nrf;

    for (; tmp; tmp = tmp->next)
	  {
		struct fh_store *ele = (struct fh_store *)tmp->item;
		nrf = ele->sizesList.isRfExist(rf);
		if (nrf > 0)
		  {
			break;
		  }
	  }
	return (nrf);
}

int
FontObject::releaseRf(struct nfrf *rf)
{
	wfListElement *tmp = head;
	int ret = 0;

    for (; tmp; tmp = tmp->next)
	  {
		struct fh_store *ele = (struct fh_store *)tmp->item;
		ret += ele->sizesList.removeRf(rf);
	  }

	return (ret);
}

int
FontObject::isWebFont()
{
	return (iswebfont);
}

const char *
FontObject::url()
{
	return (urlOfFont);
}


int
FontObject::GetState()
{
	return (state);
}


int
FontObject::setState(int completion_state)
{
	int old_state = state;
	state = completion_state;
	return (old_state);
}



#include "Pcf.h"
//
// The Font garbage Collector.
//
// A FontHandle can be released when there are no consumers using this
// FontObject and there are no rf's active. This is given by the expression
//		[referenceCount(FONT) - fonthandleCount(FONT)]
//				+ rfcount(FONT.sizesList)				== 0
// referenceCount(FONT) is got from looking into the nff* jmc object
// fonthandleCount(FONT) is got by count()
//
// A FontObject can be destroyed only when there are no fonthandles
// in the FontObject. This is given by the expression:
//		fonthandleCount(FONT) == 0
//
// This will return ZERO, if the Font can be deleted. If not, it
// will return NONZERO.
//
int FontObject::
GC()
{
	// Prevent the GC from calling itself. This will happen when fonthandles
	// are removed. Since each of the list element refcounts the Font, everytime
	// an element it removed, its freeItem() will Font::release().
	// The below will prevent the GC from stepping over itself.
	if (inGC)
	{
		return(-1);
	}
	inGC++;

	wfListElement *tmp = head;

	cfImplHeader* impl = (cfImplHeader*)cf2cfImpl(self);
	int nconsumers = impl->refcount - count();

	// At this point, we will increment the refcount by 1. This is because
	// removing an fh could cause the refcount to goto zero and that would
	// cause the FontObject that we are working with to be deleted from
	// underneath us. To prevent this, **AFTER COMPUTING nconsumers** we
	// increment the refcount.
	impl->refcount++;

    while(tmp)
	{
		struct fh_store *ele = (struct fh_store *)tmp->item;
		//
		// WARNING: we could be possibly removing this element.
		// Store the next element we should visit ahead of time.
		//
		tmp = tmp->next;

		if (nconsumers + ele->sizesList.getRfCount() == 0)
		{
			// This fonthandle is a candidate for deletion.
			remove(ele);
		}
	}

	// Undo the dummy incrementing of refcount we did to protect ourself.
	// NOTE: we cannot use nff_release() as that would go and delete the
	// object. Callers of the GC are smart enough to check the return value
	// of GC or look at the refcount and delete the object.
	impl->refcount--;

	inGC --;
	return (impl->refcount);
}

//
// Private method implementations.
//

void FontObject::
computeSizes(struct nfrc *rc, struct fh_store *ele)
{
#if defined(XP_WIN) && !defined(WIN32) && !defined(XP_OS2)
	/* Win 16 core dumps on  enumerating. HACK HACK HACK */
	return;
#else
	if (!ele->sizesList.initialized())
	  {
		// Sizes was never computed for this
		WF_TRACEMSG(("NF: Computing sizes supported by font 0x%x by displayer %s.\n",
			self, ele->fppeer->name()));
		jdouble *sizes = ele->fppeer->EnumerateSizes(rc, ele->fh);
		ele->sizesList.addSizes(sizes);
	  }
#endif
}

//
// Other functions
//
void
free_fh_store(wfList *object, void *item)
{
	// We know that the wfList is actually a FontObject
	// because FontObject is the superclass and wfList is the
	// base class.
	//
	// Hence this is ok.
	FontObject *fob = (FontObject *)object;
	struct fh_store *ele = (struct fh_store *)item;

	// Tell the wffpPeer that we are done with the the fonthandle
	// The wffpPeer will take decisions of unloading the displayer
	// DLM on this call.
	ele->fppeer->FontHandleDone(ele->fh);


	delete(ele);

	//
	// We will have the fonthandle refcount the Font too. So
	// deletion of a fontHandle will decrement the refcount of Font.
	// A font should not be deleted until all its fonthandles
	// are deleted.
	//
	nff_release(fob->self, NULL);
}

