/* -*- 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.
 */
/* 
 *
 * All function that are of general use in libfont/ are implemented here.
 *
 * dp Suresh <dp@netscape.com>
 */


#include "libfont.h"
#include "nf.h"
#include "wfMisc.h"
#include "Mnffmi.h"

#ifdef NSPR20
#ifdef XP_MAC
#include "probslet.h"
#else
#include "obsolete/probslet.h"
#endif
#endif


// The logic we are using to merge these is brute force.
// For each size in newSizes list, if it is already in the oldSizes,
// then we wont add it. If not we will add it in.
// We should make sure the we are using the public memory allocator
// here because this memory is expected to be freed by the caller.
#define INTS_AT_A_TIME 128
void
MergeSizes(jdouble * &oldSizes, int &oldLenMax, jdouble *newSizes)
{
    int newlen = 0;
    
    if (!newSizes)
      {
		return;
      }
    
    while (newSizes[newlen] >= 0)
      {
		int i = 0;
		int found = 0;
		if (oldSizes == NULL)
		  {
			oldSizes = (jdouble *) WF_ALLOC(sizeof(*oldSizes) * INTS_AT_A_TIME);
			if (!oldSizes) return;
			oldSizes[0] = -1;
			oldLenMax = INTS_AT_A_TIME;
		  }
		for (;oldSizes[i] >= 0; i++)
		  {
			if (newSizes[newlen] == oldSizes[i])
			  {
				found++;
				break;
			  }
		  }
		if (!found)
		  {
			// Add newSizes[newlen] to oldSizes
			if (i >= oldLenMax-1)
			  {
				// Need more memory.
				oldSizes = (jdouble *)
				  WF_REALLOC(oldSizes,
							 sizeof(*oldSizes) * (oldLenMax + INTS_AT_A_TIME));
				if (!oldSizes) return;
				oldLenMax += INTS_AT_A_TIME;
			  }
			oldSizes[i++] = newSizes[newlen];
			oldSizes[i++] = -1;
		  }
		newlen++;
      }
}

//
// CopyString
//
// Make a copy of the input C-String and return a new C++ allocated
// C-String.
//
char *
CopyString(const char *str)
{
	char *newStr = NULL;
	int len = 0;
	const char *tmp1;
	char *tmp2;

	if (str && *str)
	  {
		for (tmp1=str; *tmp1; len++, tmp1++);
		newStr = new char[len+1];
		if (newStr)
		  {
			for (tmp1=str, tmp2=newStr; *tmp1; *tmp2++ = *tmp1++);
			*tmp2 = '\0';
		  }
	  }
	return (newStr);
}


//
// Returns a newly allocated string that Escapes characters {:-*\}
// in the input str.
// For eg.
//	input string	: string-to*be-escaped\n
//	return		: string\-to\*be\-escaped\\n
//
// WARNING: Caller must delete the return string.

char *
EscapeString(const char *str, const char *escapeThese)
{
	if (!str || !*str)
	{
		return NULL;
	}
	char *newstr = new char [2*strlen(str)+1];
	if (!newstr)
	{
		// ERROR: No memory
		return (NULL);
	}
	char* t = newstr;
	while (*str)
	{
		if (strchr(escapeThese, *str))
			*t++ = '\\';
		*t++ = *str++;
	}
	*t = '\0';
	return (newstr);
}

//
// ScanToken
//  Scans a token delimited by 'stopChars' from 'str' and copies it out
//	into 'buf'
//	By default this compresses multiple spaces in the token to a single space.
//	If 'noSpacesOnOutput' is set, all spaces, leading/trailing/intermediate
//	spaces are stripped.
//
//	Returns a pointer to the character after the token in 'str'
//
const char *
wf_scanToken(const char *str, char *buf, int len, char *stopChars,
			int noSpacesOnOutput)
{
	int pos = 0;

	str = wf_skipSpaces(str);

    while (*str != '\0')
	{
		while (*str != '\0' && ! isspace(*str) &&
			(stopChars == NULL || strchr(stopChars, *str) == NULL))
		{
			buf[pos++] = *str++;
        }

		str = wf_skipSpaces(str);
		if (*str == '\0' || stopChars == NULL ||
			strchr(stopChars, *str) != NULL)
		{
			break;
        }
		if (! noSpacesOnOutput)
		{
			buf[pos++] = ' ';
		}
	}
	buf[pos] = '\0';
	return str;
}

//
// wf_scanVariableAndValue
// Modifies buf such that variable and value point to the the LHS and RHS of
// a buf of the format " variable = value "
// Leading and trailing spaces for both variable and value are trimmed.
//
int
wf_scanVariableAndValue(char *buf, int buflen, char *&variable, char *&value)
{
	variable = buf;
	value = NULL;
	while (*buf && *buf != '=')
	{
		buf ++;
	}
	if ('=' == *buf)
	{
		*buf = '\0';
		buf++;
		value = buf;
	}
	variable = wf_trimSpaces(variable);
	value = wf_trimSpaces(value);
	return (0);
}

/*
 * wf_StringEndsWith
 *
 * returns nonzero if string 's' ends with string 'endstr'
 * else returns zero
 */
int
wf_stringEndsWith(const char *s, const char *endstr)
{
  int l, lend;
  int retval = 0;

  if (!endstr)
  {
	  /* All strings ends in NULL */
	  retval = 1;
  }
  else if (!s)
  {
	  /* NULL strings will never have endstr at its end */
	  retval = 0;
  }
  else
  {
	  lend = strlen(endstr);
	  l = strlen(s);
#ifdef XP_WIN
	  if (l >= lend && !stricmp(s+l-lend, endstr))  /* ignore case for windows */
#else
      if (l >= lend && !strcmp(s+l-lend, endstr))
#endif
	  {
		  retval = 1;
	  }
  }
  return (retval);
}


//
// wf_skipSpaces
// Returns a pointer to the first non-space character in 'str'
//
const char *
wf_skipSpaces(const char *str)
{
    while (*str != '\0' && isspace(*str))
	{
        str++;
	}
    return str;
}       


//
// wf_trimSpaces
// Trims initial and trailing spaces from the input string.
//
char *
wf_trimSpaces(char *inputString)
{
	char *str = inputString;

	if (!str || !*str)
	{
		return (str);
	}

	// Fix trailing spaces
	int nstr = strlen(str);
	int len = nstr;
	while (len && isspace(str[len-1]))
	{
		--len;
	}
	if (len < nstr)
	{
		str[len] = '\0';
		nstr = len+1;
	}

	// Fix leading spaces
	while (*str && isspace(*str))
	{
		str++;
		nstr--;
	}
	if (str != inputString)
	{
		memmove(inputString, str, nstr+1);
	}
	
	return (inputString);
}

//
// Returns
// 0	: if both strings are the same on a case insensitive compare
// -1	: if any of the string were NULL
// 1	: if string 'two' is a case insensitive substring of string 'one'
// 2	: if string 'one' is a case insensitive substring of string 'two'
// 3	: if case insensitive compare failed
//
int
wf_strcasecmp(const char *one, const char *two)
{
	if (!one || !two)
	{
		return (-1);
	}

	for(; *one && *two; one++, two++)
	{
		if ((*one != *two) && (tolower(*one) != tolower(*two)))
			return (3);
	}
	if (*one)
		return (1);
	if (*two)
		return (2);
	return 0;
}

//
// return 0 if the 2 strings are same on case insensitive compare upto
//		atmost n bytes
// else -1
//
int
wf_strncasecmp(const char *one, const char *two, int n)
{
	if (!one || !two)
	{
		return (-1);
	}

	for(; *one && *two && n-- > 0; one++, two++)
	{
		if ((*one != *two) && (tolower(*one) != tolower(*two)))
			return (-1);
	}

	if (n <= 0 || (*one == '\0' && *two == '\0'))
		return (0);
	else
		return (-1);
}


// returns a pointer to the extension part of the input string
const char *wf_getExtension(const char *fullname)
{
	const char *ext = NULL;
	if (!fullname || !*fullname)
	{
		return (ext);
	}

	while (*fullname)
	{
		if (*fullname == '.')
		{
			ext = fullname+1;
		}
		fullname++;
	}
	return(ext);
}

int wf_addToString(char **str, int *len, int *maxlen, const char *s)
{
	if (!s || !*s)
	{
		return (0);
	}

	if (!maxlen || !str)
	{
		// Invalid params.
		return (-1);
	}

	int slen = strlen(s);
	int curlen = 0;
	if (!len)
	{
		curlen = strlen(*str);
	}
	else
	{
		curlen = *len;
	}

	if (curlen + slen + 1 > *maxlen)
	{
		int newlen = WF_ROUND_INT(curlen+slen+1, WF_STRING_ALLOCATION_STEP);
		*str = (char *)WF_REALLOC(*str, sizeof(char)*newlen);
		if (!*str)
		{
			// No memory
			return (-1);
		}
		*maxlen = newlen;
	}
	memcpy((*str)+curlen, s, slen+1);

	if (len)
	{
		*len += slen;
	}

	return (0);
}

//
// Webfont related miscellaneous routines
//

int
wf_releaseFmis(struct nffmi **fmis)
{
	if (!fmis)
	{
		return (-1);
	}
	while (*fmis)
	{
		nffmi_release(*fmis, NULL);
		fmis++;
	}
	return (0);	
}


//-----------------
// File operations
//-----------------

int wf_isFileReadable(const char *filename)
{
	if (!filename || !*filename)
		return (-1);

	FILE *fp = fopen(filename, "r");
	int readable = (fp != NULL);
	fclose(fp);

	return (readable);
}

int wf_isFileDirectory(const char *filename)
{
	if (!filename || !*filename)
		return (0);

	PRFileInfo finfo;

	if (PR_GetFileInfo(filename, &finfo) == PR_FAILURE)
		return (0);

	return (finfo.type == PR_FILE_DIRECTORY);
}


//
// expandFileName:
//
// expands the following in the filename
// beginning ~	: HOME
//
// Returns the no: of expansions done. If none was done, returns 0.
// Returns -ve if there was no space to do the expansion.
//
extern int wf_expandFilename(char *filename, int maxlen)
{
	int ret = 0;

	// Expand initial ~ to HOME
	if (*filename == '~')
	{
		const char *home = getenv("HOME");
		if (home && *home)
		{
			// Insert home into filename
			int homelen = strlen(home);
			int len = strlen(filename);
			
			if (len+homelen <= maxlen)
			{
				// Shift all characters in filename by homelen bytes
				memmove(filename+homelen, filename+1, len);
				// Copy home into filename
				memcpy(filename, home, homelen);
				ret ++;
			}
			else
			{
				ret = -1;
			}
		}
	}
	return (ret);
}
