// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Some convenience stuff
 *
 * Authors:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *   Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Kris De Gussem <Kris.DeGussem@gmail.com>
 *
 * Copyright (C) 1999-2002 authors
 * Copyright (C) 2001-2002 Ximian, Inc.
 * Copyright (C) 2010 Jasper van de Gronde
 * Copyright (C) 2010 Jon A. Cruz
 * Copyright (C) 2012 Kris De Gussem
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <cstring>
#include <gdk/gdk.h>
#include <map>
#include <sstream>

#include "color.h"
#include "sp-cursor.h"

static void free_cursor_data(unsigned char *pixels, void* /*data*/) {
    delete [] reinterpret_cast<guint32*>(pixels);
}

struct RGBA {
    guchar v[4];

    RGBA() { 
        v[0] = 0;
        v[1] = 0;
        v[2] = 0;
        v[3] = 0;
    }

    RGBA(guchar r, guchar g, guchar b, guchar a) {
        v[0] = r;
        v[1] = g;
        v[2] = b;
        v[3] = a;
    }

    operator guint32() const {
        guint32 result = (static_cast<guint32>(v[0]) << 0)
            | (static_cast<guint32>(v[1]) << 8)
            | (static_cast<guint32>(v[2]) << 16)
            | (static_cast<guint32>(v[3]) << 24);
        return result;
    }
};

GdkCursor *sp_cursor_from_xpm(char const *const *xpm, guint32 fill, guint32 stroke)
{
    GdkPixbuf *pixbuf;
    GdkCursor *cursor = nullptr;
    GdkDisplay *display = gdk_display_get_default();

    int height = 0;
    int width = 0;
    int colors = 0;
    int pix = 0;
    int hot_x = 0;
    int hot_y = 0;
    std::stringstream ss (std::stringstream::in | std::stringstream::out);
    ss << xpm[0];
    ss >> height;
    ss >> width;
    ss >> colors;
    ss >> pix;
    ss >> hot_x;
    ss >> hot_y;
    
    if (gdk_display_supports_cursor_alpha(display) && gdk_display_supports_cursor_color(display)) {
        std::map<char, RGBA> colorMap;

        for (int i = 0; i < colors; i++) {

            char const *p = xpm[1 + i];
            g_assert(*p >=0);
            unsigned char const ccode = (guchar) *p;

            p++;
            while (isspace(*p)) {
                p++;
            }
            p++;
            while (isspace(*p)) {
                p++;
            }

            if (strcmp(p, "Fill") == 0) {
                colorMap[ccode] = RGBA(SP_RGBA32_R_U(fill), SP_RGBA32_G_U(fill), SP_RGBA32_B_U(fill), SP_RGBA32_A_U(fill));
            } else if (strcmp(p, "Stroke") == 0) {
                colorMap[ccode] = RGBA(SP_RGBA32_R_U(stroke), SP_RGBA32_G_U(stroke), SP_RGBA32_B_U(stroke), SP_RGBA32_A_U(stroke));
            } else if (p[0] == '#') {
                GdkRGBA color;
                if (gdk_rgba_parse(&color, p)) {
                    colorMap[ccode] = RGBA(color.red * 255, color.green * 255, color.blue * 255, color.alpha * 255);
                } else {
                    colorMap[ccode] = RGBA();
                }
            } else { // Catches 'None'
                colorMap[ccode] = RGBA();
            }
        }

        guint32 *pixmap_buffer = new guint32[width * height];

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                std::map<char, RGBA>::const_iterator it = colorMap.find(xpm[1 + colors + y][x]);
                pixmap_buffer[y * width + x] = (it == colorMap.end()) ? 0u : it->second;
            }
        }

#if G_BYTE_ORDER == G_BIG_ENDIAN
        for (int i = 0, n = width * height; i < n; i++) {
            guint32 v = pixmap_buffer[i];
            pixmap_buffer[i] = ((v & 0xFF) << 24) | (((v >> 8) & 0xFF) << 16) | (((v >> 16) & 0xFF) << 8) | ((v >> 24) & 0xFF);
        }
#endif

        pixbuf = gdk_pixbuf_new_from_data(reinterpret_cast<guchar*>(pixmap_buffer), GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * sizeof(guint32), free_cursor_data, nullptr);
    } else {
	pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **)xpm);
    }

    if (pixbuf != nullptr) {
	cursor = gdk_cursor_new_from_pixbuf(display, pixbuf, hot_x, hot_y);
        g_object_unref(pixbuf);
    } else {
        g_warning("Failed to load cursor from xpm!");
    }
    return cursor;
}

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
