#include "config.h"
#include <Imlib2.h>

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "filter_common.h"

#define GET_INT(x, ptr) (((ptr)->type == VAR_PTR) ?     \
                        (x) = (*(int *)(ptr)->data) :   \
                        ((ptr)->type == VAR_CHAR) ?     \
                        (x) = strtol((ptr)->data, 0, 0) \
                        : 0)

#define GET_DOUBLE(x, ptr) (((ptr)->type == VAR_PTR) ?  \
                        (x) = (*(double *)(ptr)->data) :        \
                        ((ptr)->type == VAR_CHAR) ?     \
                        (x) = strtod((ptr)->data, 0)    \
                        : 0)

static void
mod_brightness(double t[256], double v)
{
    int             i;

    for (i = 256; --i >= 0;)
        t[i] += v;
}

static void
mod_contrast(double t[256], double v)
{
    int             i;

    for (i = 256; --i >= 0;)
        t[i] = ((t[i] - 0.5) * v) + 0.5;
}

static void
mod_gamma(double t[256], double v)
{
    int             i;

    for (i = 256; --i >= 0;)
        t[i] = pow(t[i], 1 / v);
}

static void
mod_tint(double t[256], double v)
{
    int             i;

    for (i = 256; --i >= 0;)
        t[i] *= v;
}

static          Imlib_Image
colormod(Imlib_Image im, IFunctionParam *par)
{
    double          a_d[256], r_d[256], g_d[256], b_d[256];
    uint8_t         a_b[256], r_b[256], g_b[256], b_b[256];
    IFunctionParam *ptr;
    int             x = 0, y = 0, h, w, i;
    double          v = 0.0;

    imlib_context_set_image(im);
    w = imlib_image_get_width();
    h = imlib_image_get_height();

    for (i = 256; --i >= 0;)
        a_d[i] = r_d[i] = g_d[i] = b_d[i] = (double)i / 255;

    for (ptr = par; ptr; ptr = ptr->next)
    {
        if (!strcmp("x", ptr->key))
        {
            GET_INT(x, ptr);
        }
        else if (!strcmp("y", ptr->key))
        {
            GET_INT(y, ptr);
        }
        else if (!strcmp("w", ptr->key))
        {
            GET_INT(w, ptr);
        }
        else if (!strcmp("h", ptr->key))
        {
            GET_INT(h, ptr);
        }
        else if (!memcmp("brightness", ptr->key, 10))
        {
            GET_DOUBLE(v, ptr);
            if (!ptr->key[10])
            {
                mod_brightness(r_d, v);
                mod_brightness(g_d, v);
                mod_brightness(b_d, v);
                mod_brightness(a_d, v);
            }
            else if (!strcmp("_r", ptr->key + 10))
            {
                mod_brightness(r_d, v);
            }
            else if (!strcmp("_g", ptr->key + 10))
            {
                mod_brightness(g_d, v);
            }
            else if (!strcmp("_b", ptr->key + 10))
            {
                mod_brightness(b_d, v);
            }
            else if (!strcmp("_a", ptr->key + 10))
            {
                mod_brightness(a_d, v);
            }
        }
        else if (!memcmp("contrast", ptr->key, 8))
        {
            GET_DOUBLE(v, ptr);
            if (!ptr->key[8])
            {
                mod_contrast(r_d, v);
                mod_contrast(g_d, v);
                mod_contrast(b_d, v);
                mod_contrast(a_d, v);
            }
            else if (!strcmp("_r", ptr->key + 8))
            {
                mod_contrast(r_d, v);
            }
            else if (!strcmp("_g", ptr->key + 8))
            {
                mod_contrast(g_d, v);
            }
            else if (!strcmp("_b", ptr->key + 8))
            {
                mod_contrast(b_d, v);
            }
            else if (!strcmp("_a", ptr->key + 8))
            {
                mod_contrast(a_d, v);
            }
        }
        else if (!memcmp("gamma", ptr->key, 5))
        {
            GET_DOUBLE(v, ptr);
            if (!ptr->key[5])
            {
                mod_gamma(r_d, v);
                mod_gamma(g_d, v);
                mod_gamma(b_d, v);
                mod_gamma(a_d, v);
            }
            else if (!strcmp("_r", ptr->key + 5))
            {
                mod_gamma(r_d, v);
            }
            else if (!strcmp("_g", ptr->key + 5))
            {
                mod_gamma(g_d, v);
            }
            else if (!strcmp("_b", ptr->key + 5))
            {
                mod_gamma(b_d, v);
            }
            else if (!strcmp("_a", ptr->key + 5))
            {
                mod_gamma(a_d, v);
            }
        }
        else if (!memcmp("tint", ptr->key, 4))
        {
            GET_DOUBLE(v, ptr);
            if (!ptr->key[4])
            {
                mod_tint(r_d, v);
                mod_tint(g_d, v);
                mod_tint(b_d, v);
                mod_tint(a_d, v);
            }
            else if (!strcmp("_r", ptr->key + 4))
            {
                mod_tint(r_d, v);
            }
            else if (!strcmp("_g", ptr->key + 4))
            {
                mod_tint(g_d, v);
            }
            else if (!strcmp("_b", ptr->key + 4))
            {
                mod_tint(b_d, v);
            }
            else if (!strcmp("_a", ptr->key + 4))
            {
                mod_tint(a_d, v);
            }
        }
    }
    for (i = 256; --i >= 0;)
    {
        if (a_d[i] < 0)
            a_d[i] = 0;
        if (a_d[i] > 1)
            a_d[i] = 1;
        a_b[i] = a_d[i] * 255;
        if (r_d[i] < 0)
            r_d[i] = 0;
        if (r_d[i] > 1)
            r_d[i] = 1;
        r_b[i] = r_d[i] * 255;
        if (g_d[i] < 0)
            g_d[i] = 0;
        if (g_d[i] > 1)
            g_d[i] = 1;
        g_b[i] = g_d[i] * 255;
        if (b_d[i] < 0)
            b_d[i] = 0;
        if (b_d[i] > 1)
            b_d[i] = 1;
        b_b[i] = b_d[i] * 255;
    }
    imlib_context_set_color_modifier(imlib_create_color_modifier());
    imlib_set_color_modifier_tables(r_b, g_b, b_b, a_b);
    imlib_apply_color_modifier_to_rectangle(x, y, w, h);
    imlib_free_color_modifier();
    return im;
}

void
init(ImlibFilterInfo *info)
{
    static const char *const filters[] = { "colormod" };
    int             i = ARRAY_SIZE(filters);

    info->name = strdup("Tinting");
    info->author = strdup("Willem Monsuwe (willem@stack.nl)");
    info->description =
        strdup("Provides most common color modification filters.");
    info->num_filters = i;
    info->filters = malloc(sizeof(char *) * i);
    while (--i >= 0)
        info->filters[i] = strdup(filters[i]);
}

void
deinit(void)
{
    return;
}

void           *
exec(char *filter, void *im, IFunctionParam *par)
{
    if (!strcmp(filter, "colormod"))
        return colormod((Imlib_Image) im, par);
    return im;
}
