#include "common.h"

#include <stdlib.h>

#include "filter.h"
#include "image.h"

/*\ Create and return an empty filter struct \*/
ImlibFilter    *
__imlib_CreateFilter(int size)
{
    ImlibFilter    *fil;

    fil = malloc(sizeof(ImlibFilter));
    if (size > 0)
    {
        fil->alpha.pixels = malloc(size * sizeof(ImlibFilterPixel));
        fil->red.pixels = malloc(size * sizeof(ImlibFilterPixel));
        fil->green.pixels = malloc(size * sizeof(ImlibFilterPixel));
        fil->blue.pixels = malloc(size * sizeof(ImlibFilterPixel));
        fil->alpha.size = size;
        fil->red.size = size;
        fil->green.size = size;
        fil->blue.size = size;
    }
    else
    {
        fil->alpha.pixels = 0;
        fil->red.pixels = 0;
        fil->green.pixels = 0;
        fil->blue.pixels = 0;
        fil->alpha.size = 0;
        fil->red.size = 0;
        fil->green.size = 0;
        fil->blue.size = 0;
    }
    fil->alpha.entries = 0;
    fil->red.entries = 0;
    fil->green.entries = 0;
    fil->blue.entries = 0;
    fil->alpha.div = 0;
    fil->red.div = 0;
    fil->green.div = 0;
    fil->blue.div = 0;
    fil->alpha.cons = 0;
    fil->red.cons = 0;
    fil->green.cons = 0;
    fil->blue.cons = 0;
    return fil;
}

/*\ Free a filter struct \*/
void
__imlib_FreeFilter(ImlibFilter *fil)
{
    free(fil->alpha.pixels);
    free(fil->red.pixels);
    free(fil->green.pixels);
    free(fil->blue.pixels);
    free(fil);
}

void
__imlib_FilterSetColor(ImlibFilterColor *fil, int x, int y,
                       int a, int r, int g, int b)
{
    int             i;
    ImlibFilterPixel *pix = fil->pixels;

    /*\ Look for an entry matching (x, y) \ */
    for (i = fil->entries; --i >= 0;)
    {
        if ((pix[i].xoff == x) && (pix[i].yoff == y))
            break;
    }
    /*\ If all zero, remove the found entry \ */
    if (!(a | r | g | b))
    {
        if (i >= 0)
        {
            while (i < fil->entries)
            {
                pix[i] = pix[i + 1];
            }
            fil->entries--;
        }
        return;
    }
    /*\ No match, then make a new entry \ */
    if (i < 0)
        i = fil->entries;
    if (i >= fil->size)
    {
        fil->size += 4;
        pix = realloc(pix, (fil->size * sizeof(ImlibFilterPixel)));
        if (!pix)
            return;
        fil->pixels = pix;
    }
    if (i >= fil->entries)
        fil->entries = i + 1;
    pix[i].xoff = x;
    pix[i].yoff = y;
    pix[i].a = a;
    pix[i].r = r;
    pix[i].g = g;
    pix[i].b = b;
}

/*\ Set the divisors manually \*/
void
__imlib_FilterDivisors(ImlibFilter *fil, int a, int r, int g, int b)
{
    fil->alpha.div = a;
    fil->red.div = r;
    fil->green.div = g;
    fil->blue.div = b;
}

/*\ Set the constants \*/
void
__imlib_FilterConstants(ImlibFilter *fil, int a, int r, int g, int b)
{
    fil->alpha.cons = a;
    fil->red.cons = r;
    fil->green.cons = g;
    fil->blue.cons = b;
}

static int
__imlib_FilterCalcDiv(ImlibFilterColor *fil)
{
    int             i, ret;
    ImlibFilterPixel *pix;

    if (fil->div)
        return fil->div;
    ret = 0;
    pix = fil->pixels;
    for (i = fil->entries; --i >= 0;)
    {
        ret += pix->a + pix->r + pix->g + pix->b;
        pix++;
    }
    return ret;
}

static int
__imlib_FilterGet(ImlibFilterColor *fil, uint32_t *data,
                  int w, int h, int x, int y)
{
    int             i, off, ret;
    ImlibFilterPixel *pix;
    uint32_t       *p;

    ret = fil->cons;
    pix = fil->pixels;
    for (i = fil->entries; --i >= 0;)
    {
        off = x + pix->xoff;
        if (off < 0)
            off = 0;
        if (off >= w)
            off = w - 1;
        p = data + off;
        off = y + pix->yoff;
        if (off < 0)
            off = 0;
        if (off >= h)
            off = h - 1;
        p += off * w;
        ret += A_VAL(p) * pix->a + R_VAL(p) * pix->r +
            G_VAL(p) * pix->g + B_VAL(p) * pix->b;
        pix++;
    }
    return ret;
}

/*\ Correct saturation from [-32768, 32767] to [0, 255] \*/
#define SATURATE(x) ((((x) | (!((x) >> 8) - 1)) & (~((x) >> 31))) & 0xff)

/*\ Filter an image with the a, r, g, b filters in fil
|*|  NB: This is currently not very optimal, and could probably be improved
\*/
void
__imlib_FilterImage(ImlibImage *im, ImlibFilter *fil)
{
    int             x, y, a, r, g, b, ad, rd, gd, bd;
    uint32_t       *data, *p1, *p2;

    data = malloc(im->w * im->h * sizeof(uint32_t));
    if (!data)
        return;

    ad = __imlib_FilterCalcDiv(&fil->alpha);
    rd = __imlib_FilterCalcDiv(&fil->red);
    gd = __imlib_FilterCalcDiv(&fil->green);
    bd = __imlib_FilterCalcDiv(&fil->blue);

    p1 = im->data;
    p2 = data;

    for (y = 0; y < im->h; y++)
    {
        for (x = 0; x < im->w; x++)
        {
            *p2 = *p1;
            if (ad)
            {
                a = __imlib_FilterGet(&fil->alpha, im->data, im->w, im->h, x,
                                      y);
                a /= ad;
                A_VAL(p2) = SATURATE(a);
            }
            if (rd)
            {
                r = __imlib_FilterGet(&fil->red, im->data, im->w, im->h, x, y);
                r /= rd;
                R_VAL(p2) = SATURATE(r);
            }
            if (gd)
            {
                g = __imlib_FilterGet(&fil->green, im->data, im->w, im->h, x,
                                      y);
                g /= gd;
                G_VAL(p2) = SATURATE(g);
            }
            if (bd)
            {
                b = __imlib_FilterGet(&fil->blue, im->data, im->w, im->h, x, y);
                b /= bd;
                B_VAL(p2) = SATURATE(b);
            }
            p1++;
            p2++;
        }
    }
    __imlib_ReplaceData(im, data);
}
