#include <qpainter.h>
#include <qpixmap.h>
#include <qfontmetrics.h>
#include <qfile.h>
#include <qtextstream.h>
#include <kurl.h>
#include <kconfig.h>
#include <kglobalsettings.h>
#include <kio/netaccess.h>
#include <math.h>
#include "FcEngine.h"
#include "KfiConstants.h"
#ifdef HAVE_XFT
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include <fixx11h.h>
#endif

#define KFI_HAVE_OBLIQUE         // Do we differentiate between Italic and Oblique?
#define KFI_HAVE_MEDIUM_WEIGHT   // Do we differentiate between Medium and Normal weights?

#define KFI_PREVIEW_GROUP      "Preview Settings"
#define KFI_PREVIEW_STRING_KEY "String"

#ifdef HAVE_XFT
#define KFI_DISPLAY(pix) (pix ? pix->x11Display() : QPaintDevice::x11AppDisplay())
#endif

namespace KFI
{

const int CFcEngine::constScalableSizes[]={8, 10, 12, 24, 36, 48, 64, 72, 96, 0 };
const int CFcEngine::constDefaultAlphaSize=24;

static int fcWeight(int weight)
{
    if(weight<FC_WEIGHT_ULTRALIGHT)
        return FC_WEIGHT_THIN;

    if(weight<(FC_WEIGHT_ULTRALIGHT+FC_WEIGHT_LIGHT)/2)
        return FC_WEIGHT_ULTRALIGHT;

    if(weight<(FC_WEIGHT_LIGHT+FC_WEIGHT_NORMAL)/2)
        return FC_WEIGHT_LIGHT;

#ifdef KFI_HAVE_MEDIUM_WEIGHT
    if(weight<(FC_WEIGHT_NORMAL+FC_WEIGHT_MEDIUM)/2)
        return FC_WEIGHT_NORMAL;

    if(weight<(FC_WEIGHT_MEDIUM+FC_WEIGHT_SEMIBOLD)/2)
        return FC_WEIGHT_MEDIUM;
#else
    if(weight<(FC_WEIGHT_NORMAL+FC_WEIGHT_SEMIBOLD)/2)
        return FC_WEIGHT_NORMAL;
#endif

    if(weight<(FC_WEIGHT_SEMIBOLD+FC_WEIGHT_BOLD)/2)
        return FC_WEIGHT_SEMIBOLD;

    if(weight<(FC_WEIGHT_BOLD+FC_WEIGHT_ULTRABOLD)/2)
        return FC_WEIGHT_BOLD;

    if(weight<(FC_WEIGHT_ULTRABOLD+FC_WEIGHT_HEAVY)/2)
        return FC_WEIGHT_ULTRABOLD;

    return FC_WEIGHT_HEAVY;
}

static int fcToQtWeight(int weight)
{
    switch(weight)
    {
        case FC_WEIGHT_THIN:
            return 0;
        case FC_WEIGHT_ULTRALIGHT:
            return QFont::Light>>1;
        case FC_WEIGHT_LIGHT:
            return QFont::Light;
        default:
        case FC_WEIGHT_NORMAL:
            return QFont::Normal;
        case FC_WEIGHT_MEDIUM:
#ifdef KFI_HAVE_MEDIUM_WEIGHT
            return (QFont::Normal+QFont::DemiBold)>>1;
#endif
            return QFont::Normal;
        case FC_WEIGHT_SEMIBOLD:
            return QFont::DemiBold;
        case FC_WEIGHT_BOLD:
            return QFont::Bold;
        case FC_WEIGHT_ULTRABOLD:
            return (QFont::Bold+QFont::Black)>>1;
        case FC_WEIGHT_HEAVY:
            return QFont::Black;
    }
}

#ifndef KFI_FC_NO_WIDTHS
static int fcWidth(int width)
{
    if(width<FC_WIDTH_EXTRACONDENSED)
        return FC_WIDTH_ULTRACONDENSED;

    if(width<(FC_WIDTH_EXTRACONDENSED+FC_WIDTH_CONDENSED)/2)
        return FC_WIDTH_EXTRACONDENSED;

    if(width<(FC_WIDTH_CONDENSED+FC_WIDTH_SEMICONDENSED)/2)
        return FC_WIDTH_CONDENSED;

    if(width<(FC_WIDTH_SEMICONDENSED+FC_WIDTH_NORMAL)/2)
        return FC_WIDTH_SEMICONDENSED;

    if(width<(FC_WIDTH_NORMAL+FC_WIDTH_SEMIEXPANDED)/2)
        return FC_WIDTH_NORMAL;

    if(width<(FC_WIDTH_SEMIEXPANDED+FC_WIDTH_EXPANDED)/2)
        return FC_WIDTH_SEMIEXPANDED;

    if(width<(FC_WIDTH_EXPANDED+FC_WIDTH_EXTRAEXPANDED)/2)
        return FC_WIDTH_EXPANDED;

    if(width<(FC_WIDTH_EXTRAEXPANDED+FC_WIDTH_ULTRAEXPANDED)/2)
        return FC_WIDTH_EXTRAEXPANDED;

    return FC_WIDTH_ULTRAEXPANDED;
}

static int fcToQtWidth(int weight)
{
    switch(weight)
    {
        case FC_WIDTH_ULTRACONDENSED:
            return QFont::UltraCondensed;
        case FC_WIDTH_EXTRACONDENSED:
            return QFont::ExtraCondensed;
        case FC_WIDTH_CONDENSED:
            return QFont::Condensed;
        case FC_WIDTH_SEMICONDENSED:
            return QFont::SemiCondensed;
        default:
        case FC_WIDTH_NORMAL:
            return QFont::Unstretched;
        case FC_WIDTH_SEMIEXPANDED:
            return QFont::SemiExpanded;
        case FC_WIDTH_EXPANDED:
            return QFont::Expanded;
        case FC_WIDTH_EXTRAEXPANDED:
            return QFont::ExtraExpanded;
        case FC_WIDTH_ULTRAEXPANDED:
            return QFont::UltraExpanded;
    }
}
#endif

static int fcSlant(int slant)
{
    if(slant<FC_SLANT_ITALIC)
        return FC_SLANT_ROMAN;

#ifdef KFI_HAVE_OBLIQUE
    if(slant<(FC_SLANT_ITALIC+FC_SLANT_OBLIQUE)/2)
        return FC_SLANT_ITALIC;

    return FC_SLANT_OBLIQUE;
#else
    return FC_SLANT_ITALIC;
#endif
}

static bool fcToQtSlant(int slant)
{
    return FC_SLANT_ROMAN==slant ? false : true;
}

static int fcSpacing(int spacing)
{
    if(spacing<FC_MONO)
        return FC_PROPORTIONAL;

    if(spacing<(FC_MONO+FC_CHARCELL)/2)
        return FC_MONO;

    return FC_CHARCELL;
}

static int strToWeight(const QString &str, QString &newStr)
{
    if(0==str.find(i18n(KFI_WEIGHT_THIN), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_THIN).length());
        return FC_WEIGHT_THIN;
    }
    if(0==str.find(i18n(KFI_WEIGHT_EXTRALIGHT), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_EXTRALIGHT).length());
        return FC_WEIGHT_EXTRALIGHT;
    }
    if(0==str.find(i18n(KFI_WEIGHT_ULTRALIGHT), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_ULTRALIGHT).length());
        return FC_WEIGHT_ULTRALIGHT;
    }
    if(0==str.find(i18n(KFI_WEIGHT_LIGHT), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_LIGHT).length());
        return FC_WEIGHT_LIGHT;
    }
    if(0==str.find(i18n(KFI_WEIGHT_REGULAR), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_REGULAR).length());
        return FC_WEIGHT_REGULAR;
    }
    if(0==str.find(i18n(KFI_WEIGHT_NORMAL), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_NORMAL).length());
        return FC_WEIGHT_NORMAL;
    }
    if(0==str.find(i18n(KFI_WEIGHT_MEDIUM), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_MEDIUM).length());
        return FC_WEIGHT_MEDIUM;
    }
    if(0==str.find(i18n(KFI_WEIGHT_DEMIBOLD), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_DEMIBOLD).length());
        return FC_WEIGHT_SEMIBOLD;
    }
    if(0==str.find(i18n(KFI_WEIGHT_SEMIBOLD), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_SEMIBOLD).length());
        return FC_WEIGHT_SEMIBOLD;
    }
    if(0==str.find(i18n(KFI_WEIGHT_BOLD), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_BOLD).length());
        return FC_WEIGHT_BOLD;
    }
    if(0==str.find(i18n(KFI_WEIGHT_EXTRABOLD), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_EXTRABOLD).length());
        return FC_WEIGHT_EXTRABOLD;
    }
    if(0==str.find(i18n(KFI_WEIGHT_ULTRABOLD), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_ULTRABOLD).length());
        return FC_WEIGHT_ULTRABOLD;
    }
    if(0==str.find(i18n(KFI_WEIGHT_BLACK), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_BLACK).length());
        return FC_WEIGHT_BLACK;
    }
    if(0==str.find(i18n(KFI_WEIGHT_HEAVY), 0, false))
    {
        newStr=str.mid(i18n(KFI_WEIGHT_HEAVY).length());
        return FC_WEIGHT_HEAVY;
    }

    newStr=str;
    return FC_WEIGHT_REGULAR;
}

#ifndef KFI_FC_NO_WIDTHS
static int strToWidth(const QString &str, QString &newStr)
{
    if(0==str.find(i18n(KFI_WIDTH_ULTRACONDENSED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_ULTRACONDENSED).length());
        return FC_WIDTH_ULTRACONDENSED;
    }
    if(0==str.find(i18n(KFI_WIDTH_EXTRACONDENSED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_EXTRACONDENSED).length());
        return FC_WIDTH_EXTRACONDENSED;
    }
    if(0==str.find(i18n(KFI_WIDTH_CONDENSED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_CONDENSED).length());
        return FC_WIDTH_CONDENSED;
    }
    if(0==str.find(i18n(KFI_WIDTH_SEMICONDENSED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_SEMICONDENSED).length());
        return FC_WIDTH_SEMICONDENSED;
    }
    if(0==str.find(i18n(KFI_WIDTH_NORMAL), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_NORMAL).length());
        return FC_WIDTH_NORMAL;
    }
    if(0==str.find(i18n(KFI_WIDTH_SEMIEXPANDED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_SEMIEXPANDED).length());
        return FC_WIDTH_SEMIEXPANDED;
    }
    if(0==str.find(i18n(KFI_WIDTH_EXPANDED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_EXPANDED).length());
        return FC_WIDTH_EXPANDED;
    }
    if(0==str.find(i18n(KFI_WIDTH_EXTRAEXPANDED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_EXTRAEXPANDED).length());
        return FC_WIDTH_EXTRAEXPANDED;
    }
    if(0==str.find(i18n(KFI_WIDTH_ULTRAEXPANDED), 0, false))
    {
        newStr=str.mid(i18n(KFI_WIDTH_ULTRAEXPANDED).length());
        return FC_WIDTH_ULTRAEXPANDED;
    }

    newStr=str;
    return FC_WIDTH_NORMAL;
}
#endif

static int strToSlant(const QString &str)
{
    if(-1!=str.find(i18n(KFI_SLANT_ITALIC)))
        return FC_SLANT_ITALIC;
    if(-1!=str.find(i18n(KFI_SLANT_OBLIQUE)))
        return FC_SLANT_OBLIQUE;
    return FC_SLANT_ROMAN;
}

static void drawText(QPainter &painter, int x, int y, int width, const QString &str)
{
    QString s(str);
    bool    addedElipses=false;

    width-=x*2;
    while(s.length()>3 && painter.fontMetrics().size(0, s).width()>width)
    {
        if(!addedElipses)
        {
            s.remove(s.length()-2, 2);
            s.append("...");
            addedElipses=true;
        }
        else
            s.remove(s.length()-4, 1);
    }
    painter.drawText(x, y, s);
}

inline bool equal(double d1, double d2)
{
    return (fabs(d1 - d2) < 0.0001);
}

inline bool equalWeight(int a, int b)
{
    return a==b || fcWeight(a)==fcWeight(b);
}

#ifndef KFI_FC_NO_WIDTHS
inline bool equalWidth(int a, int b)
{
    return a==b || fcWidth(a)==fcWidth(b);
}
#endif

inline bool equalSlant(int a, int b)
{
    return a==b || fcSlant(a)==fcSlant(b);
}

#ifdef HAVE_XFT
static bool drawChar(QPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const QString &text, int pos,
                     int &x, int &y, int w, int h, int fSize, int offset)
{
    XGlyphInfo     extents;
    const FcChar16 *str=(FcChar16 *)(&(text.ucs2()[pos]));

    XftTextExtents16(pix.x11Display(), xftFont, str, 1, &extents);

    if(x+extents.width+2>w)
    {
        x=offset;
        y+=fSize;
    }

    if(y+offset<h)
    {
        XftDrawString16(xftDraw, xftCol, xftFont, x, y, str, 1);
        x+=extents.width+2;
        return true;
    }
    return false;
}

static bool drawString(QPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const QString &text,
                       int x, int &y, int h, int offset)
{   
    XGlyphInfo     extents;
    const FcChar16 *str=(FcChar16 *)(text.ucs2());

    XftTextExtents16(pix.x11Display(), xftFont, str, text.length(), &extents);
    if(y+extents.height<h)
        XftDrawString16(xftDraw, xftCol, xftFont, x, y+extents.y, str, text.length());
    if(extents.height>0)
    {
        y+=extents.height+offset;
        return true;
    }
    return false;
}

static bool drawGlyph(QPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, FT_UInt i,
                      int &x, int &y, int &w, int &h, int fSize, int offset)
{
    XGlyphInfo extents;

    XftGlyphExtents(pix.x11Display(), xftFont, &i, 1, &extents);

    if(x+extents.width+2>w)
    {
        x=offset;
        y+=fSize;
    }

    if(y+offset<h)
    {
        XftDrawGlyphs(xftDraw, xftCol, xftFont, x, y, &i, 1);
        x+=extents.width+2;
        return true;
    }
    return false;
}

inline int point2Pixel(int point)
{
    return (point*QPaintDevice::x11AppDpiX()+36)/72;
}

static bool hasStr(XftFont *font, QString &str)
{
    unsigned int slen=str.length(),
                 ch;

    for(ch=0; ch<slen; ++ch)
        if(!FcCharSetHasChar(font->charset, str[ch].unicode()))
            return false;
    return true;
}
#endif

CFcEngine::CFcEngine()
         : itsIndex(-1),
           itsIndexCount(1)
{
}

CFcEngine::~CFcEngine()
{
    // Clear any fonts that may have been added...
    FcConfigAppFontClear(FcConfigGetCurrent());
}

QString CFcEngine::getName(const KURL &url, int faceNo)
{
    if(url!=itsLastUrl || faceNo!=itsIndex)
        parseUrl(url, faceNo);

    return itsDescriptiveName;
}

#ifdef HAVE_XFT
bool CFcEngine::draw(const KURL &url, int w, int h, QPixmap &pix, int faceNo, bool thumb)
{
    bool rv=false;

    if((url==itsLastUrl && faceNo==itsIndex) || parseUrl(url, faceNo))
    {
        rv=true;

        if(!itsInstalled)  // Then add to fontconfig's list, so that Xft can display it...
        {
            FcInitReinitialize();
            FcConfigAppFontAddFile(FcConfigGetCurrent(), (const FcChar8 *)(itsName.utf8().data()));
        }

        if(thumb && (w!=h || h>128))
            thumb=false;

        int offset=thumb
                       ? h<=32
                             ? 2
                             : 3
                       : 4,
            x=offset, y=offset;

        pix.resize(w, h);
        pix.fill(Qt::white);

        QPainter painter(&pix);

        getSizes(&pix);

        if(itsSizes.size())
        {
            XRenderColor xrenderCol;
            XftColor     xftCol;

            xrenderCol.red=xrenderCol.green=xrenderCol.blue=0;
            xrenderCol.alpha=0xffff;
            XftColorAllocValue(pix.x11Display(), DefaultVisual(pix.x11Display(),
                               pix.x11Screen()), 
                               DefaultColormap(pix.x11Display(), pix.x11Screen()),
                               &xrenderCol, &xftCol);

            XftDraw *xftDraw=XftDrawCreate(pix.x11Display(), (Pixmap)(pix.handle()),
                                           (Visual*)(pix.x11Visual()), pix.x11Colormap());

            if(xftDraw)
            {
                XftFont *xftFont=NULL;
                bool    drawGlyphs=false;

                if(thumb)
                {
                    QString text(i18n("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"));

                    //
                    // Calculate size of text...
                    int fSize= h <= 32
                                ? h-(offset*2)          // 1 line of chars...
                                : h <= 64
                                        ? (h-(offset*3))/2   // 2 lines...
                                        : (h-(offset*4))/3;  // 3 lines or more

                    if(!itsScalable) // Then need to get nearest size...
                    {
                        int bSize=fSize;

                        for(unsigned int s=0; s<itsSizes.size(); ++s)
                            if (itsSizes[s]<=fSize)
                                bSize=itsSizes[s];
                        fSize=bSize;
                    }

                    unsigned int ch;

                    xftFont=getFont(fSize, &pix);

                    y=fSize;
                    if(xftFont)
                    {
                        drawGlyphs=!hasStr(xftFont, text);

                        if(!drawGlyphs)
                            for(ch=0; ch<text.length(); ++ch)   // Display char by char so that it wraps...
                                if(!drawChar(pix, xftDraw, xftFont, &xftCol, text, ch, x, y, w, h, fSize, offset))
                                    break;
                        if(drawGlyphs)
                        {
                            FT_Face face=XftLockFace(xftFont);

                            if(face)
                            {
                                for(int i=1; i<face->num_glyphs && y<w; ++i)  // Glyph 0 is the NULL glyph
                                    if(!drawGlyph(pix, xftDraw, xftFont, &xftCol, i, x, y, w, h, fSize, offset))
                                        break;

                                XftUnlockFace(xftFont);
                            }
                        }
                    }
                }
                else
                {
                    QString lowercase(getLowercaseLetters()),
                            uppercase(getUppercaseLetters()),
                            punctuation(getPunctuation()),
                            title(itsDescriptiveName.isEmpty()
                                    ? i18n("ERROR: Could not determine font's name.")
                                    : itsDescriptiveName);

                    if(1==itsSizes.size())
                        title=i18n("%1 [1 pixel]", "%1 [%n pixels]", itsSizes[0]).arg(title);

                    painter.setFont(KGlobalSettings::generalFont());
                    painter.setPen(Qt::black);
                    y=painter.fontMetrics().height();
                    drawText(painter, x, y, w-offset, title);
                    y+=4;
                    painter.drawLine(offset, y, w-(offset+1), y);
                    y+=8;

                    bool lc=true,
                         uc=true,
                         punc=true;

                    xftFont=getFont(itsAlphaSize, &pix);
                    if(xftFont)
                    {
                        lc=hasStr(xftFont, lowercase);
                        uc=hasStr(xftFont, uppercase);
                        punc=hasStr(xftFont, punctuation);

                        drawGlyphs=!lc && !uc;

                        if(!drawGlyphs)
                        {
                            if(lc)
                                drawString(pix, xftDraw, xftFont, &xftCol, lowercase, x, y, h, offset);
                            if(uc)
                                drawString(pix, xftDraw, xftFont, &xftCol, uppercase, x, y, h, offset);
                            if(punc)
                                drawString(pix, xftDraw, xftFont, &xftCol, punctuation, x, y, h, offset);
                            XftFontClose(pix.x11Display(), xftFont);
                            if(lc || uc || punc)
                                painter.drawLine(offset, y, w-(offset+1), y);
                            y+=8;
                        }

                        QString previewString(getPreviewString());
                        bool    stop=false;

                        if(!drawGlyphs)
                        {
                            if(!lc && uc)
                                previewString=previewString.upper();
                            if(!uc && lc)
                                previewString=previewString.lower();
                        }

                        for(unsigned int s=0; s<itsSizes.size(); ++s)
                        {
                            xftFont=getFont(itsSizes[s], &pix);

                            if(xftFont)
                            {
                                if(drawGlyphs)
                                {
                                    FT_Face face=XftLockFace(xftFont);

                                    if(face)
                                    {
                                        int        space=itsSizes[s]/10;
                                        XGlyphInfo extents;

                                        if(!space)
                                            space=1;

                                        for(int i=1; i<face->num_glyphs && y<w && !stop; ++i)
                                        {
                                            XftGlyphExtents(pix.x11Display(), xftFont, (const FT_UInt *)&i, 1, &extents);

                                            if(y+extents.height>h)
                                                stop=true;
                                            else
                                            {
                                                if(x+extents.width<w)
                                                    XftDrawGlyphs(xftDraw, &xftCol, xftFont, x, y+extents.y,
                                                                  (const FT_UInt *)&i, 1);
                                                if(extents.width>0)
                                                    x+=extents.width+space;
                                            }
                                            if(x>=w || i==face->num_glyphs-1)
                                            {
                                                y+=itsSizes[s]+offset;
                                                x=offset;
                                                break;
                                            }
                                        }

                                        XftUnlockFace(xftFont);
                                    }
                                }
                                else
                                    drawString(pix, xftDraw, xftFont, &xftCol, previewString, x, y, h, offset);
                                XftFontClose(pix.x11Display(), xftFont);
                            }
                        }
                    }
                }

                XftDrawDestroy(xftDraw);
            }
        }
    }

    return rv;
}
#endif

QString CFcEngine::getPreviewString()
{
    KConfig cfg(KFI_UI_CFG_FILE);

    cfg.setGroup(KFI_PREVIEW_GROUP);

    QString str(cfg.readEntry(KFI_PREVIEW_STRING_KEY));

    return str.isEmpty() ? i18n("A sentence that uses all of the letters of the alphabet",
                                "The quick brown fox jumps over the lazy dog")
                         : str;
}

void CFcEngine::setPreviewString(const QString &str)
{
    KConfig cfg(KFI_UI_CFG_FILE);

    cfg.setGroup(KFI_PREVIEW_GROUP);
    cfg.writeEntry(KFI_PREVIEW_STRING_KEY, str);
}

QString CFcEngine::getUppercaseLetters()
{
    return i18n("All of the letters of the alphabet, uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}

QString CFcEngine::getLowercaseLetters()
{
    return i18n("All of the letters of the alphabet, lowercase", "abcdefghijklmnopqrstuvwxyz");
}

QString CFcEngine::getPunctuation()
{
    return i18n("Numbers and characters", "0123456789.:,;(*!?'/\\\")£$€%^&-+@~#<>{}[]");
}

QString CFcEngine::getFcString(FcPattern *pat, const char *val, int faceNo)
{
    QString rv;
    FcChar8 *fcStr;

    if(FcResultMatch==FcPatternGetString(pat, val, faceNo, &fcStr))
        rv=QString::fromUtf8((char *)fcStr);

    return rv;
}

QString CFcEngine::createName(FcPattern *pat, int faceNo)
{
//CPD: TODO: the names *need* to match up with kfontchooser's...
    QString name(getFcString(pat, FC_FAMILY, faceNo)),
            str;
    int     intVal;
    bool    comma=false;

    if (FcResultMatch==FcPatternGetInteger(pat, FC_WEIGHT, faceNo, &intVal))
    {
        str=weightStr(intVal);
        if(!str.isEmpty())
        {
            name+=QString(", ")+str;
            comma=true;
        }
    }

    if (FcResultMatch==FcPatternGetInteger(pat, FC_SLANT, faceNo, &intVal))
    {
        str=slantStr(intVal);
        if(!str.isEmpty())
        {
            if(!comma)
            {
                name+=QChar(',');
                comma=true;
            }
            name+=QChar(' ')+str;
        }
    }

#ifndef KFI_FC_NO_WIDTHS
    if (FcResultMatch==FcPatternGetInteger(pat, FC_WIDTH, faceNo, &intVal))
    {
        str=widthStr(intVal);
        if(!str.isEmpty())
            name+=QChar(' ')+str;
    }
#endif
 
    return name;
}

QString CFcEngine::weightStr(int weight, bool emptyNormal)
{
    switch(fcWeight(weight))
    {
        case FC_WEIGHT_THIN:
            return i18n(KFI_WEIGHT_THIN);
        case FC_WEIGHT_ULTRALIGHT:
            return i18n(KFI_WEIGHT_ULTRALIGHT);
        case FC_WEIGHT_LIGHT:
            return i18n(KFI_WEIGHT_LIGHT);
        case FC_WEIGHT_NORMAL:
            return emptyNormal ? QString::null : i18n(KFI_WEIGHT_NORMAL);
        case FC_WEIGHT_MEDIUM:
            return i18n(KFI_WEIGHT_MEDIUM);
        case FC_WEIGHT_DEMIBOLD:
            return i18n(KFI_WEIGHT_SEMIBOLD);
        case FC_WEIGHT_BOLD:
            return i18n(KFI_WEIGHT_BOLD);
        case FC_WEIGHT_ULTRABOLD:
            return i18n(KFI_WEIGHT_ULTRABOLD);
        default:
            return i18n(KFI_WEIGHT_HEAVY);
    }
}

#ifndef KFI_FC_NO_WIDTHS
QString CFcEngine::widthStr(int width, bool emptyNormal)
{
    switch(fcWidth(width))
    {
        case FC_WIDTH_ULTRACONDENSED:
            return i18n(KFI_WIDTH_ULTRACONDENSED);
        case FC_WIDTH_EXTRACONDENSED:
            return i18n(KFI_WIDTH_EXTRACONDENSED);
        case FC_WIDTH_CONDENSED:
            return i18n(KFI_WIDTH_CONDENSED);
        case FC_WIDTH_SEMICONDENSED:
            return i18n(KFI_WIDTH_SEMICONDENSED);
        case FC_WIDTH_NORMAL:
            return emptyNormal ? QString::null : i18n(KFI_WIDTH_NORMAL);
        case FC_WIDTH_SEMIEXPANDED:
            return i18n(KFI_WIDTH_SEMIEXPANDED);
        case FC_WIDTH_EXPANDED:
            return i18n(KFI_WIDTH_EXPANDED);
        case FC_WIDTH_EXTRAEXPANDED:
            return i18n(KFI_WIDTH_EXTRAEXPANDED);
        default:
            return i18n(KFI_WIDTH_ULTRAEXPANDED);
    }
}
#endif

QString CFcEngine::slantStr(int slant, bool emptyNormal)
{
    switch(fcSlant(slant))
    {
        case FC_SLANT_OBLIQUE:
            return i18n(KFI_SLANT_OBLIQUE);
        case FC_SLANT_ITALIC:
            return i18n(KFI_SLANT_ITALIC);
        default:
            return emptyNormal ? QString::null : i18n(KFI_SLANT_ROMAN);
    }
}

QString CFcEngine::spacingStr(int spacing)
{
    switch(fcSpacing(spacing))
    {
        case FC_MONO:
            return i18n(KFI_SPACING_MONO);
        case FC_CHARCELL:
            return i18n(KFI_SPACING_CHARCELL);
        default:
            return i18n(KFI_SPACING_PROPORTIONAL);
    }
}

bool CFcEngine::getInfo(const KURL &url, int faceNo, QString &full, QString &family, QString &foundry, QString &weight,
#ifndef KFI_FC_NO_WIDTHS
                        QString &width,
#endif
                        QString &spacing, QString &slant)
{
    if(parseUrl(url, faceNo, true))
    {
        full=itsDescriptiveName;
        if(url.isLocalFile())
        {
            int pos;

            if(-1==(pos=itsDescriptiveName.find(", ")))   // No style information...
                family=itsDescriptiveName;
            else
                family=itsDescriptiveName.left(pos);
        }
        else
            family=itsName;
        weight=weightStr(itsWeight, false);
#ifndef KFI_FC_NO_WIDTHS
        width=widthStr(itsWidth, false);
#endif
        slant=slantStr(itsSlant, false);
        spacing=spacingStr(itsSpacing);
        foundry=itsFoundry;
        return true;
    }

    return false;
}

QFont CFcEngine::getQFont(const QString &name, int size)
{
    parseName(name, 0, false);

    QFont font(itsName, size, fcToQtWeight(itsWeight), fcToQtSlant(itsSlant));

#ifndef KFI_FC_NO_WIDTHS
    font.setStretch(fcToQtWidth(itsWidth));
#endif
    return font;
}

bool CFcEngine::parseUrl(const KURL &url, int faceNo, bool all)
{
    FcInitLoadConfigAndFonts();

    // Possible urls:
    //
    //    fonts:/times.ttf
    //    fonts:/System/times.ttf
    //    file:/home/wibble/hmm.ttf
    //
    if(KFI_KIO_FONTS_PROTOCOL==url.protocol())
    {
        KIO::UDSEntry udsEntry;
        QString       name;

        FcInitReinitialize();
        if(KIO::NetAccess::stat(url, udsEntry, NULL))  // Need to stat the url to get its font name...
        {
            KIO::UDSEntry::Iterator it(udsEntry.begin()),
                                    end(udsEntry.end());

            for( ; it != end; ++it)
                if (KIO::UDS_NAME==(*it).m_uds)
                {
                    name=(*it).m_str;
                    break;
                }
        }

        if(!name.isEmpty())
        {
            parseName(name, faceNo, all); 
            itsInstalled=true;
        }
        else
            return false;
    }
    else if(url.isLocalFile())
    {
        // Now lets see if its from the thumbnail job! if so, then file will just contain the URL!
        QFile file(url.path());
        bool  isThumbnailUrl=false;

        if(file.size()<2048 && file.open(IO_ReadOnly)) // Urls should be less than 2k, and fonts usually above!
        {
            QString     thumbUrl;
            QTextStream stream(&file);

            thumbUrl=stream.readLine();
            isThumbnailUrl=0==thumbUrl.find(KFI_KIO_FONTS_PROTOCOL) && parseUrl(KURL(thumbUrl), faceNo, all);
            file.close();
        }

        if(!isThumbnailUrl)  // Its not a thumbnail, so read the real font file...
        { 
            itsName=url.path();

            int       count;
            FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(itsName).data()), 0, NULL, &count);

            itsWeight=FC_WEIGHT_NORMAL;
#ifndef KFI_FC_NO_WIDTHS
            itsWidth=FC_WIDTH_NORMAL;
#endif
            itsSlant=FC_SLANT_ROMAN;
            itsSpacing=FC_PROPORTIONAL;

            if(pat)
            {
                itsDescriptiveName=createName(pat, faceNo);

                if(all)
                {
                    FcPatternGetInteger(pat, FC_WEIGHT, faceNo, &itsWeight);
                    FcPatternGetInteger(pat, FC_SLANT, faceNo, &itsSlant);
#ifndef KFI_FC_NO_WIDTHS
                    FcPatternGetInteger(pat, FC_WIDTH, faceNo, &itsWidth);
#endif
                    FcPatternGetInteger(pat, FC_SPACING, faceNo, &itsSpacing);
                    itsFoundry=getFcString(pat, FC_FOUNDRY, faceNo);
                }

                FcPatternDestroy(pat);
            }
            else
                itsDescriptiveName=QString::null;

            itsInstalled=false;
            itsIndex=faceNo;
        }
    }
    else
        return false;

    itsLastUrl=url;
    return true;
}

void CFcEngine::parseName(const QString &name, int faceNo, bool all)
{
    int pos;

    itsDescriptiveName=name;
    itsSpacing=FC_PROPORTIONAL;
    if(-1==(pos=name.find(", ")))   // No style information...
    {
        itsWeight=FC_WEIGHT_NORMAL;
#ifndef KFI_FC_NO_WIDTHS
        itsWidth=FC_WIDTH_NORMAL;
#endif
        itsSlant=FC_SLANT_ROMAN;
        itsName=name;
    }
    else
    {
        QString style(name.mid(pos+2));

        itsWeight=strToWeight(style, style);
#ifndef KFI_FC_NO_WIDTHS
        itsWidth=strToWidth(style, style);
#endif
        itsSlant=strToSlant(style);
        itsName=name.left(pos);
    }

    if(all)
    {
        FcObjectSet *os  = FcObjectSetBuild(FC_SPACING, FC_FOUNDRY, (void *)0);
        FcPattern   *pat = FcPatternBuild(NULL,
                                            FC_FAMILY, FcTypeString, (const FcChar8 *)(itsName.utf8().data()),
                                            FC_WEIGHT, FcTypeInteger, itsWeight,
                                            FC_SLANT, FcTypeInteger, itsSlant,
#ifndef KFI_FC_NO_WIDTHS
                                            FC_WIDTH, FcTypeInteger, itsWidth,
#endif
                                            NULL);
        FcFontSet   *set = FcFontList(0, pat, os);

        FcPatternDestroy(pat);
        FcObjectSetDestroy(os);

        if(set && set->nfont)
        {
            FcPatternGetInteger(set->fonts[0], FC_SPACING, faceNo, &itsSpacing);
            itsFoundry=getFcString(set->fonts[0], FC_FOUNDRY, faceNo);
        }
    }

    itsIndex=0; // Doesn't matter, as we're gonna use font name!
    itsLastUrl=KURL();
}

#ifdef HAVE_XFT
XftFont * CFcEngine::getFont(int size, QPixmap *pix)
{
    if(itsInstalled)
        return XftFontOpen(KFI_DISPLAY(pix), 0,
                           FC_FAMILY, FcTypeString, (const FcChar8 *)(itsName.utf8().data()),
                           FC_WEIGHT, FcTypeInteger, itsWeight,
                           FC_SLANT, FcTypeInteger, itsSlant,
#ifndef KFI_FC_NO_WIDTHS
                           FC_WIDTH, FcTypeInteger, itsWidth,
#endif
                           FC_PIXEL_SIZE, FcTypeDouble, (double)size,
                           NULL);
    else
    {
        FcPattern *pattern = FcPatternBuild(NULL,
                                            FC_FILE, FcTypeString, QFile::encodeName(itsName).data(),
                                            FC_INDEX, FcTypeInteger, itsIndex,
                                            FC_PIXEL_SIZE, FcTypeDouble, (double)size,
                                            NULL);
        return XftFontOpenPattern(KFI_DISPLAY(pix), pattern);
    }
}

void CFcEngine::getSizes(QPixmap *pix)
{
    static const int constNumSizes=11;
    static const int constNumSizeRanges=2;
    static const int constSizes[constNumSizeRanges][constNumSizes]= { {8, 10, 12, 14, 16, 18, 24, 36, 48, 72, 96},
                                                                      {7,  9, 11, 13, 15, 17, 23, 35, 47, 71, 95} };
    XftFont *f=getFont(8, pix);

    itsScalable=FcTrue;

    itsSizes.clear();
    itsAlphaSize=0;

    if(f)
    {
        bool gotSizes=false;

        if(itsInstalled)
        {
            if(FcResultMatch!=FcPatternGetBool(f->pattern, FC_SCALABLE, 0, &itsScalable))
                itsScalable=FcFalse;
        }
        else
        {
            FT_Face face=XftLockFace(f);

            if(face)
            {
                itsIndexCount=face->num_faces;
                if(!(itsScalable=FT_IS_SCALABLE(face)))
                {
                    int numSizes=face->num_fixed_sizes,
                        size;

                    gotSizes=true;

                    itsSizes.reserve(numSizes);

                    for (size=0; size<numSizes; size++)
                    {
                        itsSizes.push_back(face->available_sizes[size].height);
                        if (face->available_sizes[size].height<=constDefaultAlphaSize)
                            itsAlphaSize=face->available_sizes[size].height;
                    }
                }
                XftUnlockFace(f);
            }
        }

        XftFontClose(KFI_DISPLAY(pix), f);

        //
        // Hmm... its not a scalable font, and its installed. So to get list of sizes, iterate through a list of standard
        // sizes, and ask fontconfig for a font of that sizes. Then check the retured size, family, etc is what was asked
        // for!
        if(!itsScalable && !gotSizes)
        {
            itsSizes.reserve(constNumSizes);

            for(int l=0; l<constNumSizeRanges && !gotSizes; ++l)
                for(int i=0; i<constNumSizes; ++i)
                {
                    double  px;
                    int     iv;
                    FcChar8 *str;

                    f=getFont(constSizes[l][i], pix);

                    if(f)
                    {
                        if(FcResultMatch==FcPatternGetDouble(f->pattern, FC_PIXEL_SIZE, 0, &px) && equal(constSizes[l][i], px) &&
                           FcResultMatch==FcPatternGetInteger(f->pattern, FC_WEIGHT, 0, &iv) && equalWeight(iv,itsWeight) &&
                           FcResultMatch==FcPatternGetInteger(f->pattern, FC_SLANT, 0, &iv) && equalSlant(iv, itsSlant) &&
#ifndef KFI_FC_NO_WIDTHS
                           FcResultMatch==FcPatternGetInteger(f->pattern, FC_WIDTH, 0, &iv) && equalWidth(iv, itsWidth) &&
#endif
                           FcResultMatch==FcPatternGetString(f->pattern, FC_FAMILY, 0, &str) && str &&
                           QString::fromUtf8((char *)str)==itsName)
                        {
                            itsSizes.push_back(constSizes[l][i]);
                            gotSizes=true;
                            if(constSizes[l][i]<=constDefaultAlphaSize)
                                itsAlphaSize=constSizes[l][i];
                        }
                        XftFontClose(KFI_DISPLAY(pix), f);
                    }
                }
        }
    }

    if(itsScalable)
    {
        itsSizes.reserve(constNumSizes);

        for (int i=0; constScalableSizes[i]; ++i)
            itsSizes.push_back(point2Pixel(constScalableSizes[i]));
        itsAlphaSize=constDefaultAlphaSize;
    }
}
#endif

}
