// XmbFontImp.cc for FbTk fluxbox toolkit
// Copyright (c) 2002 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#include "XmbFontImp.hh"

#include "App.hh"
#include "TextUtils.hh"
#include "StringUtil.hh"
#include "FbPixmap.hh"
#include "GContext.hh"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif //HAVE_CONFIG_H

#ifdef HAVE_SETLOCALE
#include <locale.h>
#endif // HAVE_SETLOCALE

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif // _GNU_SOURCE

#ifdef HAVE_CSTDIO
  #include <cstdio>
#else
  #include <stdio.h>
#endif
#ifdef HAVE_CSTDARG
  #include <cstdarg>
#else
  #include <stdarg.h>
#endif
#ifdef HAVE_CSTRING
  #include <cstring>
#else
  #include <string.h>
#endif

using std::string;

namespace {

const char *getFontSize(const char *pattern, int *size) {
    const char *p;
    const char *p2=0;
    int n=0;

    for (p=pattern; 1; p++) {
        if (!*p) {
            if (p2!=0 && n>1 && n<72) {
                *size = n; return p2+1;
            } else {
                *size = 16; return 0;
            }
        } else if (*p=='-') {
            if (n>1 && n<72 && p2!=0) {
                *size = n;
                return p2+1;
            }
            p2=p; n=0;
        } else if (*p>='0' && *p<='9' && p2!=0) {
            n *= 10;
            n += *p-'0';
        } else {
            p2=0; n=0;
        }
    }
}

const char *getFontElement(const char *pattern, char *buf, int bufsiz, ...) {
    const char *p, *v;
    char *p2;
    va_list va;

    va_start(va, bufsiz);
    buf[bufsiz-1] = 0;
    buf[bufsiz-2] = '*';
    while((v = va_arg(va, char *)) != 0) {
        p = FbTk::StringUtil::strcasestr(pattern, v);
        if (p) {
            strncpy(buf, p+1, bufsiz-2);
            p2 = strchr(buf, '-');
            if (p2) *p2=0;
            va_end(va);
            return p;
        }
    }
    va_end(va);
    strncpy(buf, "*", bufsiz);
    return 0;
}

XFontSet createFontSet(const char *fontname, bool& utf8mode) {
    Display *display = FbTk::App::instance()->display();
    XFontSet fs;
    char **missing;
    const char *constdef = "-";
    char *def = const_cast<char *>(constdef);
    int nmissing;
    string orig_locale = "";

#ifdef HAVE_SETLOCALE
    if (utf8mode) {
        orig_locale = setlocale(LC_CTYPE, NULL);
        if (setlocale(LC_CTYPE, "UTF-8") == NULL) {
            utf8mode = false;
        }
    }
#endif // HAVE_SETLOCALE
    fs = XCreateFontSet(display,
                        fontname, &missing, &nmissing, &def);

    if (fs && (! nmissing)) {
#ifdef HAVE_SETLOCALE
        if (utf8mode)
            setlocale(LC_CTYPE, orig_locale.c_str());
#endif // HAVE_SETLOCALE
        return fs;
    }

#ifdef HAVE_SETLOCALE
    if (! fs) {
        if (nmissing)
            XFreeStringList(missing);

        setlocale(LC_CTYPE, "C");
        fs = XCreateFontSet(display, fontname,
                            &missing, &nmissing, &def);
        setlocale(LC_CTYPE, orig_locale.c_str());
        return fs;
    }
    if (utf8mode)
        setlocale(LC_CTYPE, orig_locale.c_str());
#endif // HAVE_SETLOCALE

    // set to false because our strings won't be utf8-happy
    utf8mode = false;

    return fs;
}

}

namespace FbTk {

XmbFontImp::XmbFontImp(const char *filename, bool utf8) : m_fontset(0), m_setextents(0), m_utf8mode(utf8) {
    if (filename != 0)
        load(filename);
}

XmbFontImp::~XmbFontImp() {
    if (m_fontset != 0)
        XFreeFontSet(App::instance()->display(), m_fontset);
}

bool XmbFontImp::load(const string &fontname) {
    if (fontname.empty())
        return false;

    XFontSet set = createFontSet(fontname.c_str(), m_utf8mode);
    if (set == 0)
        return false;

    if (m_fontset != 0)
        XFreeFontSet(App::instance()->display(), m_fontset);

    m_fontset = set;
    m_setextents = XExtentsOfFontSet(m_fontset);

    return true;
}

void XmbFontImp::drawText(const FbDrawable &d, int screen, GC main_gc, const FbString &text,
                          size_t len, int x, int y, FbTk::Orientation orient) {

    if (m_fontset == 0)
        return;

    if (orient == ROT0) {
#ifdef X_HAVE_UTF8_STRING
        if (m_utf8mode) {
            Xutf8DrawString(d.display(), d.drawable(), m_fontset,
                            main_gc, x, y,
                            text.data(), len);
        } else
#endif //X_HAVE_UTF8_STRING
            {
                string localestr = text;
                localestr.erase(len, string::npos);
                localestr = FbStringUtil::FbStrToLocale(localestr);
                XmbDrawString(d.display(), d.drawable(), m_fontset,
                              main_gc, x, y,
                              localestr.data(), localestr.size());
            }
        return;
    }

    Display *dpy = App::instance()->display();

    int xpos = x, ypos = y;
    unsigned int w = d.width();
    unsigned int h = d.height();

    translateSize(orient, w, h);
    untranslateCoords(orient, xpos, ypos, w, h);

    // not straight forward, we actually draw it elsewhere, then rotate it
    FbTk::FbPixmap canvas(d.drawable(), w, h, 1);

    // create graphic context for our canvas
    FbTk::GContext font_gc(canvas);
    font_gc.setBackground(None);
    font_gc.setForeground(None);

    XFillRectangle(dpy, canvas.drawable(), font_gc.gc(), 0, 0, canvas.width(), canvas.height());
    font_gc.setForeground(1);


#ifdef X_HAVE_UTF8_STRING
    if (m_utf8mode) {
        Xutf8DrawString(dpy, canvas.drawable(), m_fontset,
                             font_gc.gc(), xpos, ypos,
                             text.data(), len);
    } else
#endif //X_HAVE_UTF8_STRING
    {
        string localestr = text;
        localestr.erase(len, string::npos);
        localestr = FbStringUtil::FbStrToLocale(localestr);
        XmbDrawString(dpy, canvas.drawable(), m_fontset,
                           font_gc.gc(), xpos, ypos,
                           localestr.data(), localestr.size());
    }

    canvas.rotate(orient);

    GC my_gc = XCreateGC(dpy, d.drawable(), 0, 0);

    XCopyGC(dpy, main_gc, GCForeground|GCBackground, my_gc);

    // vertical or upside down

    XSetFillStyle(dpy, my_gc, FillStippled);

    // vertical or upside down

    XSetFillStyle(dpy, my_gc, FillStippled);

    XSetStipple(dpy, my_gc, canvas.drawable());

    XSetTSOrigin(dpy, my_gc, 0, 0);

    XFillRectangle(dpy, d.drawable(), my_gc, 0, 0,
                   canvas.width(),
                   canvas.height());

    XFreeGC(dpy, my_gc);

}

unsigned int XmbFontImp::textWidth(const FbString &text, unsigned int len) const {
    if (m_fontset == 0)
        return 0;

    XRectangle ink, logical;
#ifdef X_HAVE_UTF8_STRING
    if (m_utf8mode) {
        Xutf8TextExtents(m_fontset, text.data(), len,
                         &ink, &logical);
        if (logical.width != 0)
            return logical.width;
    }
#endif // X_HAVE_UTF8_STRING

    string localestr = text;
    if (len > localestr.length())
        len = localestr.length();
    localestr.erase(len, string::npos);
    localestr = FbStringUtil::FbStrToLocale(localestr);
    XmbTextExtents(m_fontset, localestr.data(), localestr.size(),
                   &ink, &logical);
    return logical.width;
}

unsigned int XmbFontImp::height() const {
    if (m_fontset == 0)
        return 0;
    return m_setextents->max_ink_extent.height;
}

} // end namespace FbTk

