/* bzflag
 * Copyright (c) 1993-2025 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// Interface header
#include "ImageFont.h"

// System headers
#include <string>
#include <string.h>

// Common implementation headers
#include "bzfgl.h"
#include "bzfio.h"
#include "MediaFile.h"

ImageFont::ImageFont()
{
    for (int i = 0; i < MAX_TEXTURE_FONT_CHARS; i++)
        fontMetrics[i].charWidth = -1;

    size = -1;

    textureXSize = -1;
    textureYSize = -1;
    textureZStep = -1;
    numberOfCharacters = -1;
}

ImageFont::~ImageFont()
{
}

int ImageFont::getSize() const
{
    return size;
}

const char* ImageFont::getFaceName() const
{
    return faceName.c_str();
}

// keep a line counter for debugging
static int line;

/* read values in Key: Value form from font metrics (.fmt) files */
bool readKeyInt(OSFile &file, std::string expectedLeft, int &retval, bool newfile=false)
{
    if (newfile)
        line = 0;

    const int expsize = int(expectedLeft.size());
    std::string tmpBuf;

    // allow for blank lines with native or foreign linebreaks, comment lines
    while (tmpBuf.size() == 0 || tmpBuf[0] == '#' || tmpBuf[0] == 10 || tmpBuf[0] == 13)
    {
        tmpBuf = file.readLine();
        line++;
    }

    if (tmpBuf.substr(0, expsize) == expectedLeft && tmpBuf[expsize]==':')
    {
        long int retvalue = strtol(tmpBuf.c_str() + expsize + 1, (char **) NULL, 10);
        if (retvalue < INT_MIN || retvalue > INT_MAX)
            return false;
        retval = (int) retvalue;
        return true;
    }
    else
    {
        logDebugMessage(2,"Unexpected line in font metrics file %s, line %d (expected %s)\n",
                        file.getFileName().c_str(), line, expectedLeft.c_str());
        return false;
    }
}

// read Char: "x" entry
bool readLetter(OSFile &file, char expected)
{
    const std::string expectedLeft = "Char:";
    const int expsize = int(expectedLeft.size());
    std::string tmpBuf;

    // allow for blank lines with native or foreign linebreaks, comment lines
    while (tmpBuf.size() == 0 || tmpBuf[0] == '#' || tmpBuf[0] == 10 || tmpBuf[0] == 13)
    {
        tmpBuf = file.readLine();
        // keep a line counter
        line++;
    }

    if (tmpBuf.substr(0, expsize) == expectedLeft)
    {
        if (int(tmpBuf.size()) >= expsize+4 && tmpBuf[expsize+1]=='"' && tmpBuf[expsize+3]=='"' &&
                tmpBuf[expsize+2]==expected)
            return true;
        else
        {
            logDebugMessage(2,"Unexpected character: %s, in font metrics file %s, line %d (expected \"%c\").\n",
                            tmpBuf.c_str()+expsize, file.getFileName().c_str(), line, expected);
            return false;
        }
    }
    else
    {
        logDebugMessage(2,"Unexpected line in font metrics file %s, line %d (expected %s)\n",
                        file.getFileName().c_str(), line, expectedLeft.c_str());
        return false;
    }
}

bool ImageFont::load(OSFile &file)
{
    std::string extension = file.getExtension();

    if (extension=="")
        return false;

    texture = file.getFileName();
    std::string::size_type underscore = texture.rfind('_');
    if (underscore == std::string::npos)
    {
        logDebugMessage(1,"Unexpected font file name: %s, no _size found\n", file.getStdName().c_str());
        return false;
    }
    faceName = texture.substr(0, underscore);
    size = strtol(texture.c_str() + underscore + 1, (char **)NULL, 10);

    if (!file.open("rb"))
        return false;

    if (!readKeyInt(file, "NumChars", numberOfCharacters, true)) return false;
    if (!readKeyInt(file, "TextureWidth", textureXSize)) return false;
    if (!readKeyInt(file, "TextureHeight", textureYSize)) return false;
    if (!readKeyInt(file, "TextZStep", textureZStep)) return false;

    // clamp the maximum char count
    if (numberOfCharacters > MAX_TEXTURE_FONT_CHARS)
    {
        logDebugMessage(1,"Too many characters (%i) in %s.\n",
                        numberOfCharacters, file.getFileName().c_str());
        numberOfCharacters = MAX_TEXTURE_FONT_CHARS;
    }

    int i;
    for (i = 0; i < numberOfCharacters; i++)
    {
        // check character
        if (!readLetter(file, i + 32)) return false;

        // read metrics
        if (!readKeyInt(file, "InitialDist", fontMetrics[i].initialDist)) return false;
        if (!readKeyInt(file, "Width", fontMetrics[i].charWidth)) return false;
        if (!readKeyInt(file, "Whitespace", fontMetrics[i].whiteSpaceDist)) return false;
        if (!readKeyInt(file, "StartX", fontMetrics[i].startX)) return false;
        if (!readKeyInt(file, "EndX", fontMetrics[i].endX)) return false;
        if (!readKeyInt(file, "StartY", fontMetrics[i].startY)) return false;
        if (!readKeyInt(file, "EndY", fontMetrics[i].endY)) return false;
        fontMetrics[i].fullWidth = fontMetrics[i].initialDist +
                                   fontMetrics[i].charWidth +
                                   fontMetrics[i].whiteSpaceDist;
    }

    file.close();

    return (numberOfCharacters > 0);
}

float ImageFont::getStrLength(float scale, const char *str, int len) const
{
    int charToUse = 0;

    float totalLen = 0;

    for (int i = 0; i < len; i++)
    {
        if (str[i] < 32)
            charToUse = 32;
        else if (str[i] > numberOfCharacters + 32)
            charToUse = 32;
        else
            charToUse = str[i];

        charToUse -= 32;

        totalLen += (float)(fontMetrics[charToUse].fullWidth);
    }

    return totalLen * scale;
}

float ImageFont::getStrLength(float scale, const std::string &str) const
{
    return getStrLength(scale, str.c_str(), str.size());
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 4 ***
// c-basic-offset: 4 ***
// indent-tabs-mode: nil ***
// End: ***
// ex: shiftwidth=4 tabstop=4
