#ifndef _plan9_
#include <config.h>
#ifdef NO_MALLOC_H
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#include <stdio.h>		/*for NULL */
#include <string.h>		/*for memcpy */
#else
#include <u.h>
#include <libc.h>
#include <stdio.h>
#endif
#include <filter.h>
#include <zoom.h>
struct subdata {
    struct filter *second;
    struct image *image;
    pixel_t **currlines;
    int recal;
    int forpversion, forversion;
    number_t pre, pim;
};
void subwindow_setsecond(struct filter *f, struct filter *f1)
{
    struct subdata *s = (struct subdata *) f->data;
    s->second = f1;
}

static void myflip(struct image *image)
{
    struct subdata *s = (struct subdata *) image->data;
    flipgeneric(image);
    s->image->flip(s->image);
    s->currlines = s->image->currlines;
}

static int requirement(struct filter *f, struct requirements *r)
{
    r->nimages = 2;
    r->flags |= IMAGEDATA;
    return (f->next->action->requirement(f->next, r));
}

extern const struct filteraction threed_filter;

static int initialize(struct filter *f, struct initdata *i)
{
    struct subdata *s = (struct subdata *) f->data;
    int x;
    int val = 0;
    pixel_t **lines1, **lines2 = NULL;
    double size;
    int width, height;
    int threed = 0;
    struct filter *f1 = f;

    inhermisc(f, i);
    if (datalost(f, i))
	s->recal = 1;
    while (f1) {
	if (f1->action == &threed_filter)
	    threed = 1;
	f1 = f1->next;
    }
    f->imageversion = i->image->version;
    if (f->childimage != NULL)
	destroy_image(f->childimage);
    s->image = f->image = i->image;
    s->image->flags |= PROTECTBUFFERS;
    s->currlines = f->image->currlines;
    s->forpversion = f->image->palette->version;
    s->forversion = f->fractalc->version;
    if (f->image->width * f->image->pixelwidth <
	f->image->height * f->image->pixelheight)
	size = f->image->width * f->image->pixelwidth / 2;
    else
	size = f->image->height * f->image->pixelheight / 2;
    width = (int) (size / f->image->pixelwidth);
    height = (int) (size / f->image->pixelheight);
    /*fractalc_resize_to(f->fractalc,size,size); */
    lines1 = (pixel_t **) malloc(sizeof(*lines1) * height);
    if (f->image->nimages == 2)
	lines2 = (pixel_t **) malloc(sizeof(*lines2) * height);
    if (lines1 == NULL)
	return 0;
    if (f->image->nimages == 2 && lines2 == NULL) {
	free(lines1);
	return 0;
    }
    for (x = 0; x < height; x++) {
	lines1[x] =
	    i->image->currlines[x + (threed ? f->image->height / 3 : 0)];
	if (f->image->nimages == 2)
	    lines2[x] =
		i->image->oldlines[x +
				   (threed ? f->image->height / 3 : 0)];
    }
    if (f->image->nimages == 2)
	for (x = 0; x < f->image->height; x++) {
	    memcpy(f->image->oldlines[x], f->image->currlines[x],
		   f->image->width * f->image->bytesperpixel);
	}
    f->childimage = i->image =
	create_image_lines(width, height, f->image->nimages, lines1,
			   lines2, i->image->palette, myflip, FREELINES,
			   f->image->pixelwidth, f->image->pixelheight);
    if (i->image == NULL) {
	free(lines1);
	free(lines2);
	return 0;
    }
    f->childimage->data = s;
    x = f->previous->action->initialize(f->previous, i);
    if (!x)
	return 0;
    if (s->second != NULL) {
	i->image = f->image;
	val = s->second->action->initialize(s->second, i);
	if (!val)
	    return 0;
    }
    return (x | val);

}

static struct filter *getinstance(const struct filteraction *a)
{
    struct filter *f = createfilter(a);
    struct subdata *s = (struct subdata *) calloc(1, sizeof(*s));
    f->name = "Subwindow";
    f->data = s;
    s->second = NULL;
    return (f);
}

static void destroyinstance(struct filter *f)
{
    free(f->data);
    if (f->childimage != NULL)
	destroy_image(f->childimage);
    free(f);
}

static int doit(struct filter *f, int flags, int time)
{
    int val = 0, m, vold;
    vinfo vs;
    vrect rs;
    float wwidth, wheight;
    struct subdata *s = (struct subdata *) f->data;
    static int v;
    if (s->second != NULL
	&& (s->recal || s->forpversion != f->image->palette->version
	    || s->forversion != f->fractalc->version)) {
	int x;
	if (s->recal)
	    f->fractalc->version++;
	s->forpversion = f->image->palette->version;
	s->forversion = f->fractalc->version;
	s->recal = 1;
	val = (s->second->action->doit(s->second, flags, time));
	if (val & ANIMATION)
	    return val;
	s->recal = 0;
	if (f->image->nimages == 2)
	    for (x = 0; x < f->image->height; x++) {
		memcpy(f->image->oldlines[x], f->image->currlines[x],
		       f->image->width * f->image->bytesperpixel);
	    }
    }
    if (s->currlines != f->image->currlines && f->childimage->nimages == 2)
	flipgeneric(f->childimage), s->currlines = f->image->currlines;
    /*FIXME: ugly hack for new julia mode */
    v++;
    wwidth = f->fractalc->windowwidth;
    wheight = f->fractalc->windowheight;
    f->fractalc->windowwidth =
	f->previous->image->width * f->previous->image->pixelwidth;
    f->fractalc->windowheight =
	f->previous->image->height * f->previous->image->pixelheight;
    vs = f->fractalc->s;
    rs = f->fractalc->rs;
    f->fractalc->s = f->fractalc->currentformula->v;
    if (f->fractalc->currentformula->calculate_julia) {
	f->fractalc->s.cr = f->fractalc->s.ci = 0;
	f->fractalc->s.rr = f->fractalc->s.ri = 4;	/*FIXME should be set to real formula's bailout */
    }
    update_view(f->fractalc);
    m = f->fractalc->mandelbrot;
    vold = f->fractalc->version;
    if (s->pre != f->fractalc->pre || s->pim != f->fractalc->pim) {
	f->fractalc->version = v;
	s->pre = f->fractalc->pre;
	s->pim = f->fractalc->pim;
    }
    f->fractalc->mandelbrot = 0;
    val = f->previous->action->doit(f->previous, flags, time) | val;
    f->fractalc->mandelbrot = m;
    f->fractalc->version = vold;
    f->fractalc->s = vs;
    f->fractalc->rs = rs;
    f->fractalc->windowwidth = wwidth;
    f->fractalc->windowheight = wheight;
    return val;
}

static void myremove(struct filter *f)
{
    /*fractalc_resize_to(f->fractalc,f->queue->last->image->width*f->queue->last->image->pixelwidth,f->queue->last->image->height*f->queue->last->image->pixelheight); */
}

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


const struct filteraction subwindow_filter = {
    "Subwindow",
    "Subwindow",
    0,
    getinstance,
    destroyinstance,
    doit,
    requirement,
    initialize,
    convertupgeneric,
    convertdown,
    myremove
};
