If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle

See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// hal_font.cpp - font implementation

#include "library.h"
#include "hal.h"

/*
==============================================================================

HFixedFont IMPLEMENTATION

==============================================================================
*/

class HFixedFont : public HFont {
public:
    HPicture    *m_pic;

public:
    HFixedFont(const char *pszName);
    virtual ~HFixedFont();

    inline int Line(const char *string, int width, const char **pend);

    virtual void CharRect(const char *string, int num,
                    int align, int width, int yspacing, LRect *rc);
    virtual void StringSize(const char *string, int width, int yspacing,
                    int *pw, int *ph);

    virtual void DrawChar(int dstx, int dsty, char c,
                    int r = 255, int g = 255, int b = 255, int a = 255);
    virtual void DrawString(int dstx, int dsty, const char *string,
                    int align, int width, int yspacing,
                    int r = 255, int g = 255, int b = 255, int a = 255);
};

/*
==============
HFixedFont::HFixedFont

Load the required picture
==============
*/
HFixedFont::HFixedFont(const char *pszName)
    : HFont(pszName)
{
    m_pic = HPicture::Get(pszName, pic_redalpha, 0);
}

/*
==============
HFixedFont::~HFixedFont

Release allocated resources
==============
*/
HFixedFont::~HFixedFont()
{
    m_pic->Release();
}

/*
==============
HFixedFont::Line

Returns the width of the line in pixels, and sets a point to the first
character no longer belonging to the line
==============
*/
inline int HFixedFont::Line(const char *string, int width, const char **pend)
{
    const char *p;
    int totalw, w;

    totalw = 0;

    p = string;
    for(;;) {
        while(*p && *p != '\n' &&
             ((*p != ' ' && *p != '\t') || p == string)) p++;
        w = (p - string) * 16;

        if (width > 0 && totalw && totalw+w > width) { // wrap around
            *pend = string;
            return totalw;
        }

        totalw += w;
        string = p;

        if (!*p || *p == '\n') {
            *pend = string;
            return totalw;
        }
    }
}

/*
==============
HFixedFont::CharRect

Return the enclosing rectangle for a given char
==============
*/
void HFixedFont::CharRect(const char *string, int num, int align, int width, int yspacing,
                  LRect *rc)
{
    const char *p, *line;
    int y, w;
    int i;
    bool gotit;

    if (!(align & align_wrap))
        width = -1;

    y = 0;
    p = string;
    gotit = false;
    for(;;) {
        line = p;
        w = Line(p, width, &p);
        if (!gotit && num <= p-string) {
            i = num - (line-string);
            rc->pos[0] = i*16;
            rc->pos[1] = y;
            switch(align & halign_mask) {
            case halign_center:
                rc->pos[0] -= w / 2;
                break;
            case halign_right:
                rc->pos[0] -= w;
                break;
            }

            gotit = true;
        }
        if (!*p)
            break;

        p++;
        y += yspacing + 16;
    }
    y += 16;

    switch(align & valign_mask) {
    case valign_center:
        rc->pos[1] -= y / 2;
        break;
    case valign_bottom:
        rc->pos[1] -= y;
        break;
    }

    rc->size[0] = 16;
    rc->size[1] = 16;
}

/*
==============
HFixedFont::StringSize

Returns the rectangular size, in pixels, of the string.
==============
*/
void HFixedFont::StringSize(const char *string, int width, int yspacing,
                        int *pw, int *ph)
{
    int maxw, w, h;

    maxw = 0;
    h = 16;
    for(;;) {
        w = Line(string, width, &string);
        if (w > maxw)
            maxw = w;
        if (!*string)
            break;

        string++;
        h += yspacing + 16;
    }

    if (pw)
        *pw = maxw;
    if (ph)
        *ph = h;
}


/*
==============
HFixedFont::DrawChar

Draw a single character at the given location
==============
*/
void HFixedFont::DrawChar(int dstx, int dsty, char c, int r, int g, int b, int a)
{
    m_pic->DrawEx(dstx, dsty, (byte)c << 4, (byte)c & 0xf0, 16, 16, r, g, b, a);
}

/*
==============
HFixedFont::DrawString

Draw a colored string at the given location
==============
*/
void HFixedFont::DrawString(int dstx, int dsty, const char *string,
                        int align, int width, int yspacing,
                        int r, int g, int b, int a)
{
    const char *p;
    int x, y;
    int w;

    if (!(align & align_wrap))
        width = -1;

    if (align & valign_mask) {
        int h;

        StringSize(string, width, yspacing, 0, &h);

        switch(align & valign_mask) {
        case valign_center:
            dsty -= h / 2;
            break;
        case valign_bottom:
            dsty -= h;
            break;
        }
    }

    y = dsty;
    while(*string) {
        x = dstx;

        w = Line(string, width, &p);

        switch(align & halign_mask) {
        case halign_center:
            x -= w / 2;
            break;
        case halign_right:
            x -= w;
            break;
        }

        while(string < p) {
            m_pic->DrawEx(x, y, (byte)(*string << 4), (byte)(*string & 0xf0),
                    16, 16, r, g, b, a);
            x += 16;
            string++;
        }
        if (!*string)
            break;

        string++;
        y += yspacing + 16;
    }
}


/*
==============================================================================

GENERIC HFont IMPLEMENTATION

==============================================================================
*/

HFont *HFont::m_pList = 0;

/*
==============
HFont::HFont

Add the font to the linked list
==============
*/
HFont::HFont(const char *pszName)
{
    xstrcpy(m_szName, sizeof(m_szName), pszName);
    m_iUsed = 1;
    m_iCacheCycle = HAL::cachecycle;

    m_ppPrev = &m_pList;
    m_pNext = m_pList;
    if (m_pNext)
        m_pNext->m_ppPrev = &m_pNext;
    m_pList = this;
}

/*
==============
HFont::~HFont

Unlink the font
==============
*/
HFont::~HFont()
{
    if (m_pNext)
        m_pNext->m_ppPrev = m_ppPrev;
    *m_ppPrev = m_pNext;
}

/*
==============
HFont::CacheCycle

Evict unused fonts
==============
*/
void HFont::CacheCycle()
{
    HFont *next, *font;

    next = m_pList;
    while(next) {
        font = next;
        next = next->m_pNext;

        if (font->m_iUsed)
            font->m_iCacheCycle = HAL::cachecycle;
        else {
            if (HAL::cachecycle-font->m_iCacheCycle > 2)
                delete font;
        }
    }
}

/*
==============
HFont::Cleanup

Complain about potential font leaks, free all cached fonts
==============
*/
void HFont::Cleanup()
{
    while(m_pList) {
        if (m_pList->m_iUsed)
            L_Printf("%s: font leak\n", m_pList->m_szName);

        delete m_pList;
    }
}

/*
==============
HFont::Get

Search for the font and load it if necessary
==============
*/
HFont *HFont::Get(const char *name)
{
    HFont *font;

    for(font = m_pList; font; font = font->m_pNext) {
        if (!strcmp(font->m_szName, name)) {
            font->AddRef();
            return font;
        }
    }

    font = new HFixedFont(name);
    return font;
}