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


#include "fmi.h"

static const char *fmi_attributes[] = {
		nfFmiName,
		nfFmiCharset,
		nfFmiEncoding,
		nfFmiWeight,
		nfFmiPitch,
		nfFmiStyle,
		nfFmiUnderline,
		nfFmiStrikeOut,
		nfFmiResolutionX,
		nfFmiResolutionY,
};

static char fmi_attributes_type[] = {
		FMI_TYPE_CSTRING,
		FMI_TYPE_CSTRING,
		FMI_TYPE_CSTRING,
		FMI_TYPE_JINT,
		FMI_TYPE_JINT,
		FMI_TYPE_JINT,
		FMI_TYPE_JINT,
		FMI_TYPE_JINT,
		FMI_TYPE_JINT,
		FMI_TYPE_JINT,
};

#define MAX_STANDARD_ATTRIBUTES (sizeof(fmi_attributes)/sizeof(*fmi_attributes))

FontMatchInfoObject::
FontMatchInfoObject(const char *iname, const char *icharset,
					const char *iencoding, jint iweight, jint ipitch,
					jint istyle, jint iunderline, jint istrikeOut, jint resX, jint resY)
					: wfList(free_fmi_attr_store), stringRepresentation(NULL),
					  stringLen(0), stringMaxLen(0)
{
	addAttribute(nfFmiName, iname);
	addAttribute(nfFmiCharset, icharset);
	addAttribute(nfFmiEncoding, iencoding);
	addAttribute(nfFmiWeight, iweight);
	addAttribute(nfFmiPitch, ipitch);
	addAttribute(nfFmiStyle, istyle);
	addAttribute(nfFmiUnderline, iunderline);
	addAttribute(nfFmiStrikeOut, istrikeOut);
	addAttribute(nfFmiResolutionX, resX);
	addAttribute(nfFmiResolutionY, resY);
	WF_TRACEMSG( ("NF: Created fmi (%s).", describe()) );
}

FontMatchInfoObject::
FontMatchInfoObject(const char *reconstructString)
: wfList(free_fmi_attr_store), stringRepresentation(NULL),
  stringLen(0), stringMaxLen(0)
{
	reconstruct(reconstructString);
}

FontMatchInfoObject::
~FontMatchInfoObject()
{
	WF_TRACEMSG( ("NF: Destroying fmi (%s).", describe()) );
	releaseStringRepresentation();
}

const char ** FontMatchInfoObject::
ListAttributes()
{
	return (fmi_attributes);
}

void * FontMatchInfoObject::
GetValue(const char *attr)
{
	wfListElement *tmp = head;
	void *value = NULL;

	if (!attr)
	  {
		// Invalid input
		return (NULL);
	  }

    for (; tmp; tmp = tmp->next)
	  {
		struct fmi_attr_store *ele = (struct fmi_attr_store *)tmp->item;
		if (ele->attr && !strcmp(ele->attr, attr))
		  {
			value = ele->u.genericValue;
			break;
		  }
	  }

	return (value);
}

jint FontMatchInfoObject::
IsEquivalent(struct nffmi *fmi)
{
	void *fmi_value;
	void *this_value;
	for (int i = 0; i < MAX_STANDARD_ATTRIBUTES; i++)
	{
		fmi_value = nffmi_GetValue(fmi, fmi_attributes[i], NULL);
		this_value = GetValue(fmi_attributes[i]);
		if (fmi_attributes_type[i] == FMI_TYPE_CSTRING)
		{
			// FMI_TYPE_CSTRING
			char * fmi_str_value = (char *)fmi_value;
			char * this_str_value = (char *)this_value;
			if (fmi_str_value != NULL && *fmi_str_value != '*' &&
				this_str_value != NULL && *this_str_value != '*' &&
				!strcmp(fmi_str_value, this_str_value))
			{
				// Mismatch
				return (0);
			}
			
		}
		else
		{
			// FMI_TYPE_INT
#ifdef OSF1
                        long fmi_int_value = (long)fmi_value;
                        long this_int_value = (long)this_value;
#else
                        int fmi_int_value = (int)fmi_value;
                        int this_int_value = (int)this_value;
#endif
			if (fmi_int_value != 0 && this_int_value != 0
				&& fmi_int_value != this_int_value)
			{
				// Mismatch
				return (0);
			}
				
		}
	}
	return (1);
}


//
// Check for equality of fmi
//
int
FontMatchInfoObject::isEqual(FontMatchInfoObject *fob)
{
	// Two fmi's are equal if their string representations ARE THE SAME
	return (!strcmp(describe(), fob->describe()));
}


int FontMatchInfoObject::
reconstruct(const char *reconstructString)
{
	// Release any stringRepresentation
	releaseStringRepresentation();

	// Remove all existing attributes
	removeAll();

	// Reconstruct the FMI from the string.
	int i = 0;
	int len = strlen(reconstructString);
	char *fontpart = new char[len+1];
	if (!fontpart)
	{
		// ERROR: No Memory.
		return (-1);
	}
	const char *attr = NULL;
	const char *value = NULL;
	while (reconstructString && *reconstructString)
	{
		if (i < MAX_STANDARD_ATTRIBUTES)
		{
			reconstructString = scanFontpart(reconstructString, fontpart, attr, value);
			if (fmi_attributes_type[i] == FMI_TYPE_CSTRING)
			{
				addAttribute(fmi_attributes[i], fontpart);
			}
			else
			{
				jint ival = atoi(fontpart);
				addAttribute(fmi_attributes[i], ival);
			}
		}
		else
		{
			reconstructString = scanFontpart(reconstructString, fontpart, attr, value);
			addAttribute(attr, (char *)value);
		}
		i++;
	}
	return (0);
}

const char * FontMatchInfoObject::
describe(void)
{
	if (stringRepresentation != NULL)
	{
		return (stringRepresentation);
	}
	
	wfListElement *tmp = head;
	stringLen = 0;
	stringMaxLen = 0;
	
	for (; tmp; tmp = tmp->next)
	{
		struct fmi_attr_store *ele = (struct fmi_attr_store *)tmp->item;
		addToString(stringRepresentation, stringLen, stringMaxLen, ele);
	}
	return (stringRepresentation);
}   

//
// Private method implementations
//

int FontMatchInfoObject::
addAttribute(const char *attr, const char *value)
{
	struct fmi_attr_store *ele = new fmi_attr_store;
	if (!ele)
	  {
		// no memory
		return (-1);
	  }

	ele->attr = attr;
	ele->valueType = FMI_TYPE_CSTRING;
	ele->u.stringValue = CopyString(value);
	add(ele);

	// Sync stringRepresentation
	addToString(stringRepresentation, stringLen, stringMaxLen, ele);
	
	return (0);
}


int FontMatchInfoObject::
addAttribute(const char *attr, jint value)
{
	struct fmi_attr_store *ele = new fmi_attr_store;
	if (!ele)
	  {
		// no memory
		return (-1);
	  }

	ele->attr = attr;
	ele->valueType = FMI_TYPE_JINT;
	ele->u.intValue = value;
	add(ele);

	// Sync stringRepresentation
	addToString(stringRepresentation, stringLen, stringMaxLen, ele);
	
	return (0);
}

void
/*ARGSUSED*/
free_fmi_attr_store(wfList *object, void *item)
{
	struct fmi_attr_store *ele = (struct fmi_attr_store *)item;
	if (ele)
	{
		if (ele->valueType == FMI_TYPE_CSTRING)
		{
			// Free the copy of the string we took
			delete[] (char *)ele->u.stringValue;
		}
		delete ele;
	}
}

#define MY_BLOCK_SIZE 16
static const char needsEscape[] = {
	FMI_FONTPART_DEFAULT, FMI_FONTPART_SEPERATOR,
	FMI_FONTPART_ESCAPE, FMI_FONTPART_ATTRSEPERATOR,
	'\0',
};


int FontMatchInfoObject::
addToString(const char *&str, int &strLen, int &strMaxLen, struct fmi_attr_store *ele)
{
	const char *value;
	char *newValue = NULL;
	char strValue[64];	// XXX There must be a better way to do this.
	int ret = 0;

	addToString(str, strLen, strMaxLen, FMI_FONTPART_SEPERATOR);
	if (count() > MAX_STANDARD_ATTRIBUTES)
	{
		// Add the attribute also
		addToString(str, strLen, strMaxLen, ele->attr);
		addToString(str, strLen, strMaxLen, ":");
	}
	
	if (ele->valueType == FMI_TYPE_JINT)
	{
		strValue[0] = '\0';
		if (ele->u.intValue > 0)
		{
			sprintf(strValue, "%d", ele->u.intValue);
		}
		value = strValue;
	}
	else
	{
		// Escape {:-*} in value. These have special meaning to us.
		value = ele->u.stringValue;
		if (value && *value)
		{
			newValue = EscapeString(value, needsEscape);
			if (!newValue)
			{
				// ERROR: No memory
				return (-1);
			}
			value = newValue;
		}
	}
	ret = addToString(str, strLen, strMaxLen, value);
	if (newValue)
	{
		delete[] newValue;
	}
	return (ret);
}

int FontMatchInfoObject::
addToString(const char *&str, int &strLen, int &strMaxLen, const char *value)
{
	int valueLen = 0;
	if (!value || !*value)
	{
		// Use default symbol
		return (0);
	}
	valueLen = strlen(value);

	while ((strMaxLen - strLen) <= valueLen)
	{
		str = (char *)WF_REALLOC((char*)str,strMaxLen + MY_BLOCK_SIZE);
		if (str == NULL) return 0;
		strMaxLen += MY_BLOCK_SIZE;
	}

	strcpy((char*)(str+strLen), value);
	strLen += valueLen;
	return strLen;
}

int FontMatchInfoObject::
addToString(const char *&str, int &strLen, int &strMaxLen, const char value)
{
	if ((strMaxLen - strLen) <= 1)
	{
		str = (char *)WF_REALLOC((char*)str,strMaxLen + MY_BLOCK_SIZE);
		if (str == NULL) return 0;
		strMaxLen += MY_BLOCK_SIZE;
	}

	*((char *)(str+strLen)) = value;
	strLen++;
	*((char *)(str+strLen)) = '\0';
	return strLen;
}

int FontMatchInfoObject::
releaseStringRepresentation(void)
{
	if (stringRepresentation)
	{
		WF_FREE((char*)stringRepresentation);
		stringRepresentation = NULL;
		stringLen = stringMaxLen = 0;
	}
	return (0);
}

//
// Scans for the next fontpart from str and copies it into fontpart.
// Assumes enough memory has been allocated for fontpart.
// If value/attr are non-null, then points value and attr to their
// appropriate places in fontpart
//
const char *FontMatchInfoObject::
scanFontpart(const char *str, char *fontpart, const char *&value, const char *&attr)
{
	char *attrEnd = NULL;

	if (attr) attr = NULL;
	if (value) value = fontpart;
	if (*str == FMI_FONTPART_SEPERATOR)
		str++;
	while (*str && *str != FMI_FONTPART_SEPERATOR)
	{
		if (*str == FMI_FONTPART_ESCAPE)
		{
			str++;
		}
		else
		{
			if (*str == FMI_FONTPART_ATTRSEPERATOR)
			{
				attrEnd = fontpart+1;
			}

		}
		*fontpart++ = *str++;
	}
	*fontpart = '\0';

	if (attrEnd)
	{
		if (attr) attr = fontpart;
		*attrEnd = '\0';
		if (value) value = attrEnd+1;
	}
	return (str);
}
