/* ftglue.c: Glue code for compiling the OpenType code from
 *           FreeType 1 using only the public API of FreeType 2
 *
 * By David Turner, The FreeType Project (www.freetype.org)
 *
 * This code is explicitely put in the public domain
 *
 * See ftglue.h for more information.
 */

#include "ftglue.h"

#if 0
#  include <stdio.h>
#  define LOG(x) ftglue_log x

static void
ftglue_log( const char*   format, ... )
{
  va_list  ap;

  va_start( ap, format );
  vfprintf( stderr, format, ap );
  va_end( ap );
}

#else
#  define LOG(x) \
      do {       \
      } while (0)
#endif

/* only used internally */
static FT_Pointer
ftglue_qalloc (FT_Memory memory,
               FT_ULong  size,
               FT_Error *perror)
{
    FT_Error   error = 0;
    FT_Pointer block = NULL;

    if (size > 0) {
	block = memory->alloc (memory, size);
	if (!block)
	    error = FT_Err_Out_Of_Memory;
    }

    *perror = error;
    return block;
}

#undef QALLOC /* just in case */
#define QALLOC(ptr, size) ((ptr) = ftglue_qalloc (memory, (size), &error), error != 0)
#define FREE(_ptr)                      \
    do {                                \
	if ((_ptr)) {                   \
	    ftglue_free (memory, _ptr); \
	    _ptr = NULL;                \
	}                               \
    } while (0)

static void
ftglue_free (FT_Memory  memory,
             FT_Pointer block)
{
    if (block)
	memory->free (memory, block);
}

FTGLUE_APIDEF (FT_Long)
ftglue_stream_pos (FT_Stream stream)
{
    LOG (("ftglue:stream:pos() -> %ld\n", stream->pos));
    return stream->pos;
}

FTGLUE_APIDEF (FT_Error)
ftglue_stream_seek (FT_Stream stream,
                    FT_Long   pos)
{
    FT_Error error = 0;

    if (stream->read) {
	if (stream->read (stream, pos, 0, 0))
	    error = FT_Err_Invalid_Stream_Operation;
    } else if (pos < 0 || (FT_ULong)pos > stream->size)
	error = FT_Err_Invalid_Stream_Operation;

    if (!error)
	stream->pos = pos;
    LOG (("ftglue:stream:seek(%ld) -> %d\n", pos, error));
    return error;
}

FTGLUE_APIDEF (FT_Error)
ftglue_stream_frame_enter (FT_Stream stream,
                           FT_ULong  count)
{
    FT_Error error = FT_Err_Ok;
    FT_ULong read_bytes;

    if (stream->read) {
	/* allocate the frame in memory */
	FT_Memory memory = stream->memory;

	if (QALLOC (stream->base, count))
	    goto Exit;

	/* read it */
	read_bytes = stream->read (stream, stream->pos,
	                           stream->base, count);
	if (read_bytes < count) {
	    FREE (stream->base);
	    error = FT_Err_Invalid_Stream_Operation;
	}
	stream->cursor = stream->base;
	stream->limit = stream->cursor + count;
	stream->pos += read_bytes;
    } else {
	/* check current and new position */
	if (stream->pos >= stream->size ||
	    stream->pos + count > stream->size) {
	    error = FT_Err_Invalid_Stream_Operation;
	    goto Exit;
	}

	/* set cursor */
	stream->cursor = stream->base + stream->pos;
	stream->limit = stream->cursor + count;
	stream->pos += count;
    }

Exit:
    LOG (("ftglue:stream:frame_enter(%ld) -> %d\n", count, error));
    return error;
}

FTGLUE_APIDEF (void)
ftglue_stream_frame_exit (FT_Stream stream)
{
    if (stream->read) {
	FT_Memory memory = stream->memory;

	FREE (stream->base);
    }
    stream->cursor = 0;
    stream->limit = 0;

    LOG (("ftglue:stream:frame_exit()\n"));
}

FTGLUE_APIDEF (FT_Error)
ftglue_face_goto_table (FT_Face   face,
                        FT_ULong  the_tag,
                        FT_Stream stream,
                        FT_ULong *table_size)
{
    FT_Error error;

    LOG (("ftglue_face_goto_table( %p, %c%c%c%c, %p )\n",
          face,
          (int)((the_tag >> 24) & 0xFF),
          (int)((the_tag >> 16) & 0xFF),
          (int)((the_tag >> 8) & 0xFF),
          (int)(the_tag & 0xFF),
          stream));

    if (!FT_IS_SFNT (face)) {
	LOG (("not a SFNT face !!\n"));
	error = FT_Err_Invalid_Face_Handle;
    } else {
	/* parse the directory table directly, without using
	 * FreeType's built-in data structures
	 */
	FT_ULong offset = 0, sig;
	FT_UInt  count, nn;

	if (FILE_Seek (0) || ACCESS_Frame (4))
	    goto Exit;

	sig = GET_Tag4();

	FORGET_Frame();

	if (sig == FT_MAKE_TAG ('t', 't', 'c', 'f')) {
	    /* deal with TrueType collections */

	    LOG ((">> This is a TrueType Collection\n"));

	    if (FILE_Seek (12 + face->face_index * 4) ||
	        ACCESS_Frame (4))
		goto Exit;

	    offset = GET_ULong();

	    FORGET_Frame();
	}

	LOG (("TrueType offset = %ld\n", offset));

	if (FILE_Seek (offset + 4) ||
	    ACCESS_Frame (2))
	    goto Exit;

	count = GET_UShort();

	FORGET_Frame();

	if (FILE_Seek (offset + 12) ||
	    ACCESS_Frame (count * 16))
	    goto Exit;

	for (nn = 0; nn < count; nn++) {
	    FT_ULong tag = GET_ULong();
	    FT_ULong checksum FC_UNUSED = GET_ULong();
	    FT_ULong start = GET_ULong();
	    FT_ULong size FC_UNUSED = GET_ULong();

	    if (tag == the_tag) {
		LOG (("TrueType table (start: %ld) (size: %ld)\n", start, size));
		error = ftglue_stream_seek (stream, start);
		if (table_size)
		    *table_size = size;
		goto FoundIt;
	    }
	}
	error = FT_Err_Table_Missing;

    FoundIt:
	FORGET_Frame();
    }

Exit:
    LOG (("TrueType error=%d\n", error));

    return error;
}

#undef QALLOC
#include "fcaliastail.h"
#undef __ftglue__
