#include "config.h"
#include <cstdlib>
#include "xthread.h"
#include "filter.h"

#define PIXELWIDTH (f->image->pixelwidth) /*all distances in cm */
#define PIXELHEIGHT (f->image->pixelheight)

#define USER_DIST (60.0)
#define INDEX_DIST (0.3)
#define EYE_DIST (8.5)
#define START1 (60.0)
#define FNC(x) x

#define NCOLORS 256
#define IMAGETYPE SMALLITER
#define spixel_t pixel8_t
static int *table;
struct stereogramdata {
    int minc;
    struct palette *palette;
    struct palette *savedpalette;
};

#include "c256.h"
#define do_stereogram do_stereogram8
#include "stereod.h"

#include "hicolor.h"
#define do_stereogram do_stereogram16
#include "stereod.h"

#include "true24.h"
#define do_stereogram do_stereogram24
#include "stereod.h"

#include "truecolor.h"
#define do_stereogram do_stereogram32
#include "stereod.h"

static int requirement(struct filter *f, struct requirements *r)
{
    f->req = *r;
    r->nimages = 1;
    r->flags &= ~IMAGEDATA;
    r->supportedmask = C256 | TRUECOLOR | TRUECOLOR24 | TRUECOLOR16 | GRAYSCALE;
    return (f->next->action->requirement(f->next, r));
}

static int initialize(struct filter *f, struct initdata *i)
{
    struct stereogramdata *s = (struct stereogramdata *)f->data;
    inhermisc(f, i);
    if (s->savedpalette == NULL)
        s->savedpalette = clonepalette(i->image->palette);
    mkstereogrampalette(i->image->palette);
    if (!inherimage(f, i, TOUCHIMAGE, i->image->width / 2,
                    (i->image->height) / 2, s->palette,
                    i->image->pixelwidth * 2, i->image->pixelheight * 2))
        return 0;
    setfractalpalette(f, s->savedpalette);
    return (f->previous->action->initialize(f->previous, i));
}

static struct filter *getinstance(const struct filteraction *a)
{
    struct filter *f = createfilter(a);
    struct stereogramdata *i = (struct stereogramdata *)calloc(1, sizeof(*i));
    i->minc = 0;
    i->savedpalette = NULL;
    i->palette = createpalette(0, 65536, IMAGETYPE, 0, 65536, NULL, NULL, NULL,
                               NULL, NULL);
    f->data = i;
    f->name = "Random dot stereogram";
    return (f);
}

static void destroyinstance(struct filter *f)
{
    struct stereogramdata *i = (struct stereogramdata *)f->data;
    if (i->savedpalette != NULL)
        destroypalette(i->savedpalette);
    destroypalette(i->palette);
    destroyinheredimage(f);
    free(f);
    free(i);
}

static int doit(struct filter *f, int flags, int time)
{
    int val;
    struct stereogramdata *s = (struct stereogramdata *)f->data;
    int i, y;
    double start, maxdist, dist;
    updateinheredimage(f);
    if (f->fractalc->maxiter < NCOLORS)
        s->palette->size = f->fractalc->maxiter;
    else
        s->palette->size = NCOLORS;
    val = f->previous->action->doit(f->previous, flags, time);
    table = (int *)malloc(sizeof(int) * NCOLORS);
    dist = (f->fractalc->s.rr) / 2;
    maxdist = INDEX_DIST * FNC(f->fractalc->maxiter) + START1;
    do {
        start = dist * maxdist - INDEX_DIST * FNC(s->minc);
        maxdist *= 5;
    } while (start + INDEX_DIST * (FNC(s->minc)) < 25.0);
    if (f->fractalc->maxiter < NCOLORS)
        y = f->fractalc->maxiter;
    else
        y = NCOLORS;
    if (y < 256)
        y = 256;
    for (i = 0; i < y; i++) {
        double dist;
        if (i != 0)
            dist = i;
        else
            dist = y - 1;
        dist = INDEX_DIST * (FNC(dist)) + start;
        table[i] = (int)(EYE_DIST * dist / (dist + USER_DIST) / PIXELWIDTH);
    }
    drivercall(*f->image,
               xth_function(do_stereogram8, f, f->childimage->height),
               xth_function(do_stereogram16, f, f->childimage->height),
               xth_function(do_stereogram24, f, f->childimage->height),
               xth_function(do_stereogram32, f, f->childimage->height));
    xth_sync();
    free(table);
    return val;
}

static void convertup(struct filter *f, int *x, int *y)
{
    *y *= 2;
    *x *= 2;
    f->next->action->convertup(f->next, x, y);
}

static void convertdown(struct filter *f, int *x, int *y)
{
    *y /= 2;
    *x /= 2;
    if (f->previous != NULL)
        f->previous->action->convertdown(f->previous, x, y);
}

static void myremovefilter(struct filter *f)
{
    struct stereogramdata *s = (struct stereogramdata *)f->data;
    if (s->savedpalette != NULL) {
        restorepalette(f->image->palette, s->savedpalette);
        destroypalette(s->savedpalette);
        s->savedpalette = NULL;
    }
}

const struct filteraction stereogram_filter = {"Random dot stereogram",
                                               "stereogram",
                                               0,
                                               getinstance,
                                               destroyinstance,
                                               doit,
                                               requirement,
                                               initialize,
                                               convertup,
                                               convertdown,
                                               myremovefilter};
