/* seborrhea-font.m,
 *
 * TTF support for Seborrhea.  This is only an abstract class; you
 * need to use either AlFont or Glyph-Keeper.
 *
 * SebFont will handle these:
 *  .ttf
 *  load-font-with-sizes ("base name" px px ...)
 */

#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include "seborrhea-font.h"


static BOOL seborrhea_font_load(const char *filename, const char *child_name, unsigned int index,
				Sebum<SebContainer> *parent, DirectoryList *dirlist);
static BOOL seborrhea_font_load_with_size(const char *filename, unsigned int index,
					  unsigned int argc, Token *argv[],
					  Sebum<SebContainer> *parent, DirectoryList *dirlist);

/*--------------------------------------------------------------*/

@implementation SebFont
+ (void) load
{
    seborrhea_register_filetype_handler("ttf", seborrhea_font_load);
    seborrhea_register_metafile_command("load-font-with-sizes", seborrhea_font_load_with_size);
}

- init
{
    [super init];
    allow_resize = YES;
    return self;
}

- (BOOL) loadFromFile:(const char *)filename
{
    (void)filename;
    assert("[SebFont] loadFromFile was not overridden!" && NO);
    abort();
}

- (BOOL) loadFromMemory:(void *)buffer :(int)buffer_size
{
    (void)buffer, (void)buffer_size;
    assert("[SebFont] loadFromMemory was not overridden!" && NO);
    abort();
}

- (BOOL) setFontSize:(int)size
{
    (void)size;
    assert("[SebFont] setFontSize was not overridden!" && NO);
    abort();
}

- (BOOL) setFontSizeOnceOnly:(int)size
{
    if ([self setFontSize:size]) {
	allow_resize = NO;
	return YES;
    }
    else
	return NO;
}

	/* Dimensions. */
- (unsigned int) textHeight
{
    assert("[SebFont] textHeight was not overridden!" && NO);
    abort();
}

- (unsigned int) textLength:(const char *)str
{
    (void)str;
    assert("[SebFont] textLength was not overridden!" && NO);
    abort();
}

	/* Simple output routines. */
- (void) putChar:(unsigned int)ch To:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b
{
    (void)ch, (void)dest, (void)x, (void)y, (void)r, (void)g, (void)b;
    assert("[SebFont] putChar was not overridden!" && NO);
    abort();
}

- (void) putChar:(unsigned int)ch To:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Decoration:(enum TEXT_DECORATION)decor
{
    if (decor & DECORATION_HORIZONTAL_OUTLINE) {
	[self putChar:ch To:dest X:x-1 Y:y Colour:0:0:0];
	[self putChar:ch To:dest X:x+1 Y:y Colour:0:0:0];
    }
    if (decor & DECORATION_VERTICAL_OUTLINE) {
	[self putChar:ch To:dest X:x Y:y-1 Colour:0:0:0];
	[self putChar:ch To:dest X:x Y:y+1 Colour:0:0:0];
    }

    [self putChar:ch To:dest X:x Y:y Colour:r:g:b];
}

- (void) putString:(const char *)str To:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b
{
    (void)str, (void)dest, (void)x, (void)y, (void)r, (void)g, (void)b;
    assert("[SebFont] putString was not overridden!" && NO);
    abort();
}

- (void) putString:(const char *)str To:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Alignment:(enum TEXT_ALIGNMENT)align
{
    if (align == ALIGN_CENTRE)
	x -= [self textLength:str]/2;
    else if (align == ALIGN_RIGHT)
	x -= [self textLength:str];

    [self putString:str To:dest X:x Y:y Colour:r:g:b];
}

- (void) putString:(const char *)str To:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Decoration:(enum TEXT_DECORATION)decor
{
    if (decor & DECORATION_HORIZONTAL_OUTLINE) {
	[self putString:str To:dest X:x-1 Y:y Colour:0:0:0];
	[self putString:str To:dest X:x+1 Y:y Colour:0:0:0];
    }
    if (decor & DECORATION_VERTICAL_OUTLINE) {
	[self putString:str To:dest X:x Y:y-1 Colour:0:0:0];
	[self putString:str To:dest X:x Y:y+1 Colour:0:0:0];
    }

    [self putString:str To:dest X:x Y:y Colour:r:g:b];
}

- (void) putString:(const char *)str To:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Alignment:(enum TEXT_ALIGNMENT)align Decoration:(enum TEXT_DECORATION)decor
{
    if (align == ALIGN_CENTRE)
	x -= [self textLength:str]/2;
    else if (align == ALIGN_RIGHT)
	x -= [self textLength:str];

    [self putString:str To:dest X:x Y:y Colour:r:g:b Decoration:decor];
}

- (void) putStringTo:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b :(const char *)format,...
{
    va_list ap;
    char cat[1024];

    va_start(ap, format);
    vsnprintf(cat, sizeof cat, format, ap);
    va_end(ap);

    [self putString:cat To:dest X:x Y:y Colour:r:g:b];
}

- (void) putStringTo:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Decoration:(enum TEXT_DECORATION)decor :(const char *)format,...
{
    va_list ap;
    char cat[1024];

    va_start(ap, format);
    vsnprintf(cat, sizeof cat, format, ap);
    va_end(ap);

    [self putString:cat To:dest X:x Y:y Colour:r:g:b Decoration:decor];
}

- (void) putStringTo:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Alignment:(enum TEXT_ALIGNMENT)align :(const char *)format,...
{
    va_list ap;
    char cat[1024];

    va_start(ap, format);
    vsnprintf(cat, sizeof cat, format, ap);
    va_end(ap);

    [self putString:cat To:dest X:x Y:y Colour:r:g:b Alignment:align];
}

- (void) putStringTo:(void *)dest X:(int)x Y:(int)y Colour:(int)r :(int)g :(int)b Alignment:(enum TEXT_ALIGNMENT)align Decoration:(enum TEXT_DECORATION)decor :(const char *)format,...
{
    va_list ap;
    char cat[1024];

    va_start(ap, format);
    vsnprintf(cat, sizeof cat, format, ap);
    va_end(ap);

    [self putString:cat To:dest X:x Y:y Colour:r:g:b Alignment:align Decoration:decor];
}
@end

/*--------------------------------------------------------------*/

static Class seborrhea_font_default_class;

static SebFont *seborrhea_font_really_load(const char *filename, const char *child_name,
					   unsigned int index, DirectoryList *dirlist)
{
    SebFont *seb;
    assert(filename && child_name && dirlist);
    assert(seborrhea_font_default_class);

    seb = [[seborrhea_font_default_class alloc] initWithName:child_name];
    if (not seb)
	return nil;

    if ([[dirlist class] isReal]) {
	if (not [seb loadFromFile:filename])
	    return [seb free];
    }
    else {
	void *buffer = NULL;
	unsigned int buffer_size;

	if (not [(<VirtualDirectoryList>)dirlist openFile:filename :index :&buffer :&buffer_size])
	    return [seb free];

	if (not [seb loadFromMemory:buffer :buffer_size]) {
	    free(buffer);
	    return [seb free];
	}

	/* Note: Glyph-keeper needs to keep the data, so don't free
	   it.  SebFontGlyphKeeper will free it later. */
    }

    return seb;
}

static BOOL seborrhea_font_load(const char *filename, const char *child_name, unsigned int index,
				Sebum<SebContainer> *parent, DirectoryList *dirlist)
{
    SebFont *seb;
    assert(filename && child_name && parent && dirlist);

    seb = seborrhea_font_really_load(filename, child_name, index, dirlist);
    if (seb) {
	[parent addToList:seb];
	return YES;
    }
    else {
	fprintf(stderr, "[SebFont] Error loading %s.\n", filename);
	return NO;
    }
}

static BOOL seborrhea_font_load_with_size(const char *filename, unsigned int index,
					  unsigned int argc, Token *argv[],
					  Sebum<SebContainer> *parent, DirectoryList *dirlist)
{
    SebFont *seb;
    char name[1024];
    assert(filename && parent && dirlist);

    if (argc < 2 ||
	not [argv[0] isType:TOKEN_STRING] ||
	not [argv[1] isType:TOKEN_FIXNUM])
	return NO;

    /* XXX: This could be faster if we loaded just once. */
    while (argc > 1) {
	argc--;

	if (not [argv[argc] isType:TOKEN_FIXNUM])
	    return NO;

	snprintf(name, sizeof name, "%s%d", [argv[0] getString], [argv[argc] getFixnum]);
	seb = seborrhea_font_really_load(filename, name, index, dirlist);
	if (seb) {
	    [seb setFontSizeOnceOnly:[argv[argc] getFixnum]];
	    [parent addToList:seb];
	}
	else
	    return NO;
    }

    return YES;
}

/*--------------------------------------------------------------*/

BOOL seborrhea_font_init(Class default_class)
{
    assert(default_class);

    if (seborrhea_font_default_class &&
	seborrhea_font_default_class != default_class) {
	fprintf(stderr, "[SebFont] SebFont was already initialized by %s!  %s has been ignored.\n",
		[seborrhea_font_default_class name], [default_class name]);
	return NO;
    }
    else {
	seborrhea_font_default_class = default_class;
	return YES;
    }
}

void seborrhea_font_shutdown(void)
{
    seborrhea_font_default_class = nil;
}
