#include <Gosu/Platform.hpp>
#if defined(GOSU_IS_WIN)

#include <Gosu/IO.hpp>
#include <Gosu/Utility.hpp>
#include <cassert>
#include <cstdio>
#include <windows.h>

// Adapted from http://www.codeproject.com/KB/GDI/xfont.aspx.
// Kudos to Philip Patrick and Hans Dietrich!

struct FONT_PROPERTIES_ANSI
{
    char csName[1024];
    char csCopyright[1024];
    char csTrademark[1024];
    char csFamily[1024];
};
struct TT_OFFSET_TABLE
{
    USHORT    uMajorVersion;
    USHORT    uMinorVersion;
    USHORT    uNumOfTables;
    USHORT    uSearchRange;
    USHORT    uEntrySelector;
    USHORT    uRangeShift;
};
struct TT_TABLE_DIRECTORY
{
    char    szTag[4];            //table name
    ULONG    uCheckSum;            //Check sum
    ULONG    uOffset;            //Offset from beginning of file
    ULONG    uLength;            //length of the table in bytes
};
struct TT_NAME_TABLE_HEADER
{
    USHORT    uFSelector;            //format selector. Always 0
    USHORT    uNRCount;            //Name Records count
    USHORT    uStorageOffset;        //Offset for strings storage, from start of the table
};
struct TT_NAME_RECORD
{
    USHORT    uPlatformID;
    USHORT    uEncodingID;
    USHORT    uLanguageID;
    USHORT    uNameID;
    USHORT    uStringLength;
    USHORT    uStringOffset;    //from start of storage area
};

#define SWAPWORD(x)        MAKEWORD(HIBYTE(x), LOBYTE(x))
#define SWAPLONG(x)        MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))

#define _T(x) x
#define TRACE printf

namespace Gosu
{
std::string get_name_from_ttf_file(const std::string& filename)
{
    FONT_PROPERTIES_ANSI fp;
    FONT_PROPERTIES_ANSI * lpFontProps = &fp;
    memset(lpFontProps, 0, sizeof(FONT_PROPERTIES_ANSI));

    Buffer buffer;
    load_file(buffer, filename);

    // get the file size
    DWORD dwFileSize = buffer.size();
    LPBYTE lpMapAddress = (LPBYTE) buffer.data();

    BOOL bRetVal = FALSE;
    int index = 0;

    TT_OFFSET_TABLE ttOffsetTable;
    memcpy(&ttOffsetTable, &lpMapAddress[index], sizeof(TT_OFFSET_TABLE));
    index += sizeof(TT_OFFSET_TABLE);

    ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
    ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
    ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);

    //check is this is a true type font and the version is 1.0
    if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0) {
        throw std::runtime_error("Only version 1.0 of the TTF file format is supported");
    }

    TT_TABLE_DIRECTORY tblDir;
    memset(&tblDir, 0, sizeof(TT_TABLE_DIRECTORY));
    BOOL bFound = FALSE;
    char szTemp[4096];
    memset(szTemp, 0, sizeof(szTemp));

    for (int i = 0; i< ttOffsetTable.uNumOfTables; i++)
    {
        memcpy(&tblDir, &lpMapAddress[index], sizeof(TT_TABLE_DIRECTORY));
        index += sizeof(TT_TABLE_DIRECTORY);

        strncpy(szTemp, tblDir.szTag, 4);
        if (stricmp(szTemp, "name") == 0)
        {
            bFound = TRUE;
            tblDir.uLength = SWAPLONG(tblDir.uLength);
            tblDir.uOffset = SWAPLONG(tblDir.uOffset);
            break;
        }
        else if (szTemp[0] == 0)
        {
            break;
        }
    }

    if (bFound)
    {
        index = tblDir.uOffset;

        TT_NAME_TABLE_HEADER ttNTHeader;
        memcpy(&ttNTHeader, &lpMapAddress[index], sizeof(TT_NAME_TABLE_HEADER));
        index += sizeof(TT_NAME_TABLE_HEADER);

        ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
        ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
        TT_NAME_RECORD ttRecord;
        bFound = FALSE;

        for (int i = 0;
             i < ttNTHeader.uNRCount &&
             (lpFontProps->csCopyright[0] == 0 ||
              lpFontProps->csName[0] == 0      ||
              lpFontProps->csTrademark[0] == 0 ||
              lpFontProps->csFamily[0] == 0);
             i++)
        {
            memcpy(&ttRecord, &lpMapAddress[index], sizeof(TT_NAME_RECORD));
            index += sizeof(TT_NAME_RECORD);

            ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
            ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
            ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);

            if (ttRecord.uNameID == 1 || ttRecord.uNameID == 0 || ttRecord.uNameID == 7)
            {
                int nPos = index;

                index = tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;

                memset(szTemp, 0, sizeof(szTemp));

                memcpy(szTemp, &lpMapAddress[index], ttRecord.uStringLength);
                index += ttRecord.uStringLength;

                if (szTemp[0] != 0)
                {
                    assert (strlen(szTemp) < sizeof(lpFontProps->csName));

                    switch (ttRecord.uNameID)
                    {
                        case 0:
                            if (lpFontProps->csCopyright[0] == 0)
                                strncpy(lpFontProps->csCopyright, szTemp,
                                    sizeof(lpFontProps->csCopyright)-1);
                            break;

                        case 1:
                            if (lpFontProps->csFamily[0] == 0)
                                strncpy(lpFontProps->csFamily, szTemp,
                                    sizeof(lpFontProps->csFamily)-1);
                            bRetVal = TRUE;
                            break;

                        case 4:
                            if (lpFontProps->csName[0] == 0)
                                strncpy(lpFontProps->csName, szTemp,
                                    sizeof(lpFontProps->csName)-1);
                            break;

                        case 7:
                            if (lpFontProps->csTrademark[0] == 0)
                                strncpy(lpFontProps->csTrademark, szTemp,
                                    sizeof(lpFontProps->csTrademark)-1);
                            break;

                        default:
                            break;
                    }
                }
                index = nPos;
            }
        }
    }

    return lpFontProps->csName[0] ? lpFontProps->csName : lpFontProps->csFamily;
}
}

#endif