/*******************************************************************************
 *
 *	Functions and data structures used to maintain a single list of glyph
 *	names.  The list is sorted alphabetically and each name appears only
 *	once.  After all font information has been read, the 'index' field of
 *	each GLYPHNAME structure is initialized, so future sorts/searches can
 *	be done without comparing strings.
 *
 * Copyright 2001 Ian Pilcher
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <string.h>
#include "psdrv.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(psdrv);

#define	GLYPHLIST_ALLOCSIZE	1024

static GLYPHNAME    **glyphList = NULL;
static INT	    glyphListSize = 0;
static BOOL 	    glyphNamesIndexed = TRUE;

/*******************************************************************************
 *	PSDRV_GlyphListInit
 *
 *  Allocates initial block of memory for the glyph list and copies pointers to
 *  the AGL glyph names into it; returns 0 on success, 1 on failure
 *
 */
INT PSDRV_GlyphListInit()
{
    INT i;

    /*
     *	Compute the smallest multiple of GLYPHLIST_ALLOCSIZE that is
     *	greater than or equal to PSDRV_AGLGlyphNamesSize
     *
     */
    glyphListSize = PSDRV_AGLGlyphNamesSize;
    i = ((glyphListSize + GLYPHLIST_ALLOCSIZE - 1) / GLYPHLIST_ALLOCSIZE) *
	    GLYPHLIST_ALLOCSIZE;

    TRACE("glyphList will initially hold %i glyph names\n", i);

    glyphList = HeapAlloc(PSDRV_Heap, 0, i * sizeof(GLYPHNAME *));
    if (glyphList == NULL)
    {
	ERR("Failed to allocate %i bytes of memory\n", i * sizeof(GLYPHNAME *));
	return 1;
    }

    for (i = 0; i < glyphListSize; ++i)
	glyphList[i] = PSDRV_AGLGlyphNames + i;

    return 0;
}

/*******************************************************************************
 *	GlyphListInsert
 *
 *  Inserts a copy of the  glyph name into the list at the index, growing the
 *  list if necessary; returns index on success (-1 on failure)
 *
 */
inline static INT GlyphListInsert(LPCSTR szName, INT index)
{
    GLYPHNAME *g;

    g = HeapAlloc(PSDRV_Heap, 0, sizeof(GLYPHNAME) + strlen(szName) + 1);
    if (g == NULL)
    {
	ERR("Failed to allocate %i bytes of memory\n",
		sizeof(GLYPHNAME) + strlen(szName) + 1);
	return -1;
    }

    g->index = -1;
    g->sz = (LPSTR)(g + 1);
    strcpy((LPSTR)g->sz, szName);

    if (glyphListSize % GLYPHLIST_ALLOCSIZE == 0)	/* grow the list? */
    {
	GLYPHNAME   **newGlyphList;

	newGlyphList = HeapReAlloc(PSDRV_Heap, 0, glyphList,
		(glyphListSize + GLYPHLIST_ALLOCSIZE) * sizeof(GLYPHNAME *));
	if (newGlyphList == NULL)
	{
	    ERR("Failed to allocate %i bytes of memory\n", (glyphListSize +
		    GLYPHLIST_ALLOCSIZE) * sizeof (GLYPHNAME *));
	    HeapFree(PSDRV_Heap, 0, g);
	    return -1;
	}

	glyphList = newGlyphList;

	TRACE("glyphList will now hold %i glyph names\n",
		glyphListSize + GLYPHLIST_ALLOCSIZE);
    }

    if (index < glyphListSize)
    {
	memmove(glyphList + (index + 1), glyphList + index,
		(glyphListSize - index) * sizeof(GLYPHNAME *));
    }

    glyphList[index] = g;
    ++glyphListSize;
    glyphNamesIndexed = FALSE;

#if 0
    TRACE("Added '%s' at glyphList[%i] (glyphListSize now %i)\n",
	    glyphList[index]->sz, index, glyphListSize);
#endif
    return index;
}

/*******************************************************************************
 *	GlyphListSearch
 *
 *  Searches the specified portion of the list for the glyph name and inserts it
 *  in the list if necessary; returns the index at which the name (now) resides
 *  (-1 if unable to insert it)
 *
 */
static INT GlyphListSearch(LPCSTR szName, INT loIndex, INT hiIndex)
{
    INT midIndex, cmpResult;

    while (1)
    {
	if (loIndex > hiIndex)
	    return GlyphListInsert(szName, loIndex);

	midIndex = (loIndex + hiIndex) >> 1;

	cmpResult = strcmp(szName, glyphList[midIndex]->sz);

	if (cmpResult == 0)
	{
#if 0
	    TRACE("Found '%s' at glyphList[%i]\n", glyphList[midIndex]->sz,
		    midIndex);
#endif
	    return midIndex;
	}

	if (cmpResult < 0)
	    hiIndex = midIndex - 1;
	else
	    loIndex = midIndex + 1;
    }
}

/*******************************************************************************
 *	PSDRV_GlyphName
 *
 *  Searches the glyph name list for the provided name, adds it to the list if
 *  necessary, and returns a pointer to it (NULL if unable to add it)
 *
 */
const GLYPHNAME *PSDRV_GlyphName(LPCSTR szName)
{
    INT index;

    index = GlyphListSearch(szName, 0, glyphListSize - 1);
    if (index < 0)
	return NULL;

    return glyphList[index];
}

/*******************************************************************************
 *	PSDRV_IndexGlyphList
 *
 *  Initializes index member of all GLYPHNAME structures
 *
 */
VOID PSDRV_IndexGlyphList()
{
    INT i;

    if (glyphNamesIndexed)
    	return;

    TRACE("%i glyph names:\n", glyphListSize);

    for (i = 0; i < glyphListSize; ++i)
    {
    	glyphList[i]->index = i;
#if 0
	TRACE("  glyphList[%i] -> '%s'\n", i, glyphList[i]->sz);
#endif
    }

    glyphNamesIndexed = TRUE;
}
