/*
 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
 * Copyright (C) 2007-2021 Kim Woelders
 *
 * 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 of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * 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 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 "config.h"
#include <stdlib.h>
#include <string.h>
#include "dox.h"

static char   **
TextGetLines(const char *text, int *count)
{
    int             i, j, k;
    char          **list = NULL;

    *count = 0;
    i = 0;
    k = 0;
    if (!text)
        return NULL;
    *count = 1;
    while (text[i])
    {
        j = i;
        while ((text[j]) && (text[j] != '\n'))
            j++;
        k++;
        list = EREALLOC(char *, list, k);
        list[k - 1] = EMALLOC(char, j - i + 1);

        if (j > i)
            strncpy(list[k - 1], &(text[i]), (j - i));
        list[k - 1][j - i] = 0;
        i = j;
        if (text[i] == '\n')
            i++;
    }
    *count = k;
    return list;
}

void
TextStateLoadFont(TextState *ts)
{
    if ((ts->efont) || (ts->xfontset))
        return;

    if (!ts->fontname)
        return;

    if (!ts->efont)
    {
        char            s[4096], w[4046], *s2, *ss;

        s2 = NULL;
        s2 = strdup(ts->fontname);
        ss = strchr(s2, '/');
        if (ss)
        {
            *ss = ' ';
            word(s2, 1, s);
            word(s2, 2, w);
            ts->efont = Efont_load(s, atoi(w));
            if (ts->efont)
            {
                int             as, ds;

                Efont_extents(ts->efont, " ", &as, &ds, NULL, NULL, NULL,
                              NULL, NULL);
                ts->xfontset_ascent = as;
                ts->height = as + ds;
            }
        }
        Efree(s2);
        if (ts->efont)
            return;
    }

    if (!ts->xfontset)
    {
        int             i, missing_cnt, font_cnt;
        int             descent;
        char          **missing_list, *def_str, **fn;
        XFontStruct   **fs;

        ts->xfontset = XCreateFontSet(disp, ts->fontname, &missing_list,
                                      &missing_cnt, &def_str);
        if (!ts->xfontset)
            ts->xfontset = XCreateFontSet(disp, "fixed", &missing_list,
                                          &missing_cnt, &def_str);
        if (missing_cnt)
            XFreeStringList(missing_list);
        if (!ts->xfontset)
            return;
        font_cnt = XFontsOfFontSet(ts->xfontset, &fs, &fn);
        ts->xfontset_ascent = 0;
        for (i = 0; i < font_cnt; i++)
            ts->xfontset_ascent = MAX(fs[i]->ascent, ts->xfontset_ascent);
        descent = 0;
        for (i = 0; i < font_cnt; i++)
            descent = MAX(fs[i]->descent, descent);
        ts->height = ts->xfontset_ascent + descent;
    }
}

void
TextSize(TextState *ts, const char *text, int *width, int *height)
{
    char          **lines;
    int             i, num_lines;

    *width = 0;
    *height = 0;

    lines = TextGetLines(text, &num_lines);
    if (!lines)
        return;

    if (!ts)
        return;

    TextStateLoadFont(ts);

    if (ts->efont)
    {
        for (i = 0; i < num_lines; i++)
        {
            int             ascent, descent, wid;

            Efont_extents(ts->efont, lines[i], &ascent, &descent, &wid,
                          NULL, NULL, NULL, NULL);
            *height += ascent + descent;
            if (wid > *width)
                *width = wid;
        }
    }
    else if (ts->xfontset)
    {
        for (i = 0; i < num_lines; i++)
        {
            XRectangle      ret1, ret2;

            XmbTextExtents(ts->xfontset, lines[i], strlen(lines[i]), &ret1,
                           &ret2);
            *height += ret2.height;
            if (ret2.width > *width)
                *width = ret2.width;
        }
    }
    freestrlist(lines, num_lines);
}

void
TextDraw(TextState *ts, Window win, char *text,
         int x, int y, int w, int h __UNUSED__, int justification)
{
    char          **lines;
    int             i, num_lines;
    int             xx, yy;
    XGCValues       gcv;
    static GC       gc = 0;

    if (!ts)
        return;

    lines = TextGetLines(text, &num_lines);
    if (!lines)
        return;

    TextStateLoadFont(ts);
    yy = y;

    if (!gc)
        gc = XCreateGC(disp, win, 0, &gcv);

    if (ts->efont)
    {
        for (i = 0; i < num_lines; i++)
        {
            int             ascent, descent, wid;

            Efont_extents(ts->efont, lines[i], &ascent, &descent, &wid,
                          NULL, NULL, NULL, NULL);
            if (i == 0)
                yy += ascent;
            xx = x + (((w - wid) * justification) >> 10);
            if (ts->effect == 1)
            {
                EAllocColor(&ts->bg_col);
                XSetForeground(disp, gc, ts->bg_col.pixel);
                EFont_draw_string(disp, win, gc, xx + 1, yy + 1,
                                  lines[i], ts->efont, VRoot.vis, VRoot.cmap);
            }
            else if (ts->effect == 2)
            {
                EAllocColor(&ts->bg_col);
                XSetForeground(disp, gc, ts->bg_col.pixel);
                EFont_draw_string(disp, win, gc, xx - 1, yy,
                                  lines[i], ts->efont, VRoot.vis, VRoot.cmap);
                EFont_draw_string(disp, win, gc, xx + 1, yy,
                                  lines[i], ts->efont, VRoot.vis, VRoot.cmap);
                EFont_draw_string(disp, win, gc, xx, yy - 1,
                                  lines[i], ts->efont, VRoot.vis, VRoot.cmap);
                EFont_draw_string(disp, win, gc, xx, yy + 1,
                                  lines[i], ts->efont, VRoot.vis, VRoot.cmap);
            }
            EAllocColor(&ts->fg_col);
            XSetForeground(disp, gc, ts->fg_col.pixel);
            EFont_draw_string(disp, win, gc, xx, yy,
                              lines[i], ts->efont, VRoot.vis, VRoot.cmap);
            yy += ascent + descent;
        }
    }
    else if (ts->xfontset)
    {
        for (i = 0; i < num_lines; i++)
        {
            XRectangle      ret1, ret2;

            XmbTextExtents(ts->xfontset, lines[i], strlen(lines[i]), &ret1,
                           &ret2);
            if (i == 0)
                yy += ts->xfontset_ascent;
            xx = x + (((w - ret2.width) * justification) >> 10);
            if (ts->effect == 1)
            {
                EAllocColor(&ts->bg_col);
                XSetForeground(disp, gc, ts->bg_col.pixel);
                XmbDrawString(disp, win, ts->xfontset, gc, xx + 1, yy + 1,
                              lines[i], strlen(lines[i]));
            }
            else if (ts->effect == 2)
            {
                EAllocColor(&ts->bg_col);
                XSetForeground(disp, gc, ts->bg_col.pixel);
                XmbDrawString(disp, win, ts->xfontset, gc, xx - 1, yy,
                              lines[i], strlen(lines[i]));
                XmbDrawString(disp, win, ts->xfontset, gc, xx + 1, yy,
                              lines[i], strlen(lines[i]));
                XmbDrawString(disp, win, ts->xfontset, gc, xx, yy - 1,
                              lines[i], strlen(lines[i]));
                XmbDrawString(disp, win, ts->xfontset, gc, xx, yy + 1,
                              lines[i], strlen(lines[i]));
            }
            EAllocColor(&ts->fg_col);
            XSetForeground(disp, gc, ts->fg_col.pixel);
            XmbDrawString(disp, win, ts->xfontset, gc, xx, yy,
                          lines[i], strlen(lines[i]));
            yy += ret2.height;
        }
    }
    freestrlist(lines, num_lines);
}
