#ifdef _plan9_
#include <u.h>
#include <libc.h>
#else
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#endif
#include <fconfig.h>
#include <filter.h>
#include <ui_helper.h>
#include <archaccel.h>
#include "grlib.h"

/* This is quite simple and ugly implementation of windows.
 * it redraws all windows every frame and removes them before calculation
 * it should be OK for small windows (like texts) used by ui_helper
 * I plan to abuse it in ugly interface too, in case it will be fast enought
 */

static void
uih_darkrectangle (struct image *image, int x, int y, int width, int height)
{

    int mask = 0;
    unsigned int *current, *end;
    if (x + width < 0 || y + height < 0 || y >= image->height || x >= image->width)
        return;
    if (x + width >= image->width)
        width = image->width - x;
    if (x < 0)
        width += x, x = 0;
    if (width <= 0)
        return;
    if (y + height >= image->height)
        height = image->height - y;
    if (y < 0)
        height += y, y = 0;
    if (height <= 0)
        return;
    assert (x >= 0 && y >= 00 && width > 0 && height > 0 && x + width <= image->width && y + height <= image->height);
    if (image->bytesperpixel == 2) {
        int x1 = x / 2;
        width = (x + width + 1) / 2 - x1;
        x = x1;
    }
    if (image->bytesperpixel == 3) {
        int x1 = x * 3 / 4;
        width = (x + width + 2) * 3 / 4 - x1;
        x = x1;
    }
    switch (image->palette->type) {
        case TRUECOLOR:
        case TRUECOLOR24:
            mask = ~((1 << (image->palette->info.truec.rshift + 7 - image->palette->info.truec.rprec)) | (1 << (image->palette->info.truec.gshift + 7 - image->palette->info.truec.gprec)) | (1 << (image->palette->info.truec.bshift + 7 - image->palette->info.truec.bprec)));
            break;
        case TRUECOLOR16:
            mask = ((1 << (image->palette->info.truec.rshift + 7 - image->palette->info.truec.rprec)) | (1 << (image->palette->info.truec.gshift + 7 - image->palette->info.truec.gprec)) | (1 << (image->palette->info.truec.bshift + 7 - image->palette->info.truec.bprec)));
            mask = ~(mask | (mask << 16));
            break;
    }
    height += y;
    while (y < height) {
        current = ((unsigned int *) image->currlines[y]) + x;
        end = current + width;
        while (current < end)
            *current = (*current >> 1) & mask, current++;
        y++;
    }

}

void
uih_drawborder (struct uih_context *uih, int x, int y, int width, int height, int flags)
{
    int leftcolor;
    int rightcolor;
    int bgcolor;
    if (uih->palette->type & BITMAPS) {
        if (flags & BORDER_PRESSED) {
            bgcolor = FGCOLOR (uih);
            rightcolor = BGCOLOR (uih);
            leftcolor = BGCOLOR (uih);
        } else {
            bgcolor = BGCOLOR (uih);
            rightcolor = FGCOLOR (uih);
            leftcolor = FGCOLOR (uih);
        }
    } else {
        if (flags & BORDER_LIGHT) {
            bgcolor = LIGHTGRAYCOLOR (uih);
            rightcolor = BGCOLOR (uih);
            leftcolor = LIGHTGRAYCOLOR2 (uih);
        } else {
            bgcolor = DARKGRAYCOLOR (uih);
            rightcolor = BGCOLOR (uih);
            leftcolor = LIGHTGRAYCOLOR (uih);
        }
        if (flags & BORDER_PRESSED) {
            int i = leftcolor;
            leftcolor = rightcolor;
            rightcolor = i;
        }
        if (uih->image->flags & AAIMAGE)
            bgcolor = BGCOLOR (uih);
    }
    if (uih->image->bytesperpixel > 1 && (flags & BORDER_TRANSPARENT))
        uih_darkrectangle (uih->image, x + 1, y + 1, width - 2, height - 2) /*, leftcolor = GRAYCOLOR (uih) */ ;
    else {
        xrectangle (uih->image, x + 1, y + 1, width - 2, height - 2, bgcolor);
    }
    xhline (uih->image, x, y, width - 1, leftcolor);
    xhline (uih->image, x, y + height - 1, width - 1, rightcolor);
    xvline (uih->image, x, y, height - 1, leftcolor);
    xvline (uih->image, x + width - 1, y, height - 1, rightcolor);
}

struct uih_window *
uih_registerw (struct uih_context *uih, uih_getposfunc getpos, uih_drawfunc draw, void *data, int flags)
{
    struct uih_window *w = (struct uih_window *) calloc (1, sizeof (struct uih_window));
    struct uih_window *w1;
    assert (uih != NULL && getpos != NULL && draw != NULL && flags >= 0);
    if (w == NULL)
        return NULL;
    uih_clearwindows (uih);
    w1 = uih->wtop;
    w->getpos = getpos;
    w->draw = draw;
    w->data = data;
    w->flags = flags;
    w->savedline = -1;
    w->saveddata = NULL;
    w->flags |= BORDER_TRANSPARENT | BORDER_PRESSED | BORDER_LIGHT;
    w->next = NULL;
    if (w1 == NULL) {
        uih->wtop = w;
    } else {
        while (w1->next != NULL)
            w1 = w1->next;
        w1->next = w;
    }
    w->previous = w1;
    w->x = -65536;
    return w;
}

void
uih_setline (struct uih_context *uih, struct uih_window *w, int color, int x1, int y1, int x2, int y2)
{
    if (w->savedline != color || w->x != x1 || w->y != y1 || w->width != x2 - x1 || w->height != y2 - y1) {
        uih_clearwindows (uih);
        uih->display = 1;
        w->savedline = color;
        w->x = x1;
        w->y = y1;
        w->width = x2 - x1;
        w->height = y2 - y1;
    }
}

struct uih_window *
uih_registerline (struct uih_context *uih, int color, int x1, int y1, int x2, int y2)
{
    struct uih_window *w = (struct uih_window *) calloc (1, sizeof (struct uih_window));
    struct uih_window *w1;
    if (w == NULL)
        return NULL;
    uih_clearwindows (uih);
    w1 = uih->wtop;
    uih->display = 1;
    w->getpos = NULL;
    w->savedline = color;
    w->flags = 0;
    w->x = x1;
    w->y = y1;
    w->width = x2 - x1;
    w->height = y2 - y1;
    w->saveddata = NULL;
    w->next = NULL;
    if (w1 == NULL) {
        uih->wtop = w;
    } else {
        while (w1->next != NULL)
            w1 = w1->next;
        w1->next = w;
    }
    w->previous = w1;
    return w;
}

void
uih_removew (struct uih_context *uih, struct uih_window *w)
{
    uih_clearwindows (uih);
    assert (uih->wtop != NULL);
    assert (w != NULL);
    uih->display = 1;

    if (w->previous == NULL) {
        assert (uih->wtop == w);
        uih->wtop = w->next;
    } else {
        w->previous->next = w->next;
    }
    if (w->next != NULL) {
        w->next->previous = w->previous;
    }
    free (w);
}

/*Remove all drawed windows from screen */
void
uih_clearwindows (struct uih_context *uih)
{
    struct uih_window *w = uih->wtop;
    int savedline = 0;
    int savedpos = 0;
    int destwidth = uih->image->width * uih->image->bytesperpixel;
    if (!uih->wdisplayed)
        return;
    if (!uih->image->bytesperpixel) {
        destwidth = (w->x + uih->image->width + 7) / 8;
    }
    uih->wdisplayed = 0;
    if (uih->wflipped)
        uih->image->flip (uih->image), uih->wflipped = 0;
    while (w) {
        if (w->getpos == NULL) {
            if (w->saveddata != NULL) {
                xrestoreline (uih->image, w->saveddata, w->x, w->y, w->width + w->x, w->height + w->y);
                free (w->saveddata);
                w->saveddata = NULL;
            }
        } else {
            if (w->savedline != -1 || w->saveddata != NULL) {
                int i;
                int xskip = w->x * uih->image->bytesperpixel;
                int width = w->width * uih->image->bytesperpixel;
                if (!uih->image->bytesperpixel) {
                    xskip = w->x / 8;
                    width = (w->x + w->width + 7) / 8 - xskip;
                }
                assert (w->width);
                assert (w->height);
                assert (w->x >= 0);
                assert (w->y >= 0);
                assert (w->x + w->width <= uih->image->width);
                assert (w->y + w->height <= uih->image->height);
                if (w->savedline != -1) {
                    savedline = w->savedline;
                    savedpos = w->savedpos;
                    for (i = w->y; i < w->y + w->height; i++) {
                        unsigned char *data = uih->image->currlines[i] + xskip;
                        assert (savedline < uih->image->height);
                        assert (savedline >= 0);
                        assert (savedpos >= 0 && savedpos <= destwidth);
                        if (width + savedpos > destwidth) {
                            int width1;
                            memcpy (data, uih->image->oldlines[savedline] + savedpos, destwidth - savedpos);
                            savedline++;
                            width1 = width - destwidth + savedpos;
                            memcpy (data + (destwidth - savedpos), uih->image->oldlines[savedline], width1);
                            savedpos = width1;
                        } else
                            memcpy (data, uih->image->oldlines[savedline] + savedpos, width), savedpos += width;
                    }
                    w->savedline = -1;
                } else {
                    assert (w->saveddata);
                    for (i = w->y; i < w->y + w->height; i++) {
                        unsigned char *data = uih->image->currlines[i] + xskip;
                        memcpy (data, w->saveddata + (i - w->y) * width, width);
                    }
                    free (w->saveddata);
                    w->saveddata = NULL;
                }
            }
        }
        w = w->next;
    }
}

void
uih_drawwindows (struct uih_context *uih)
{
    struct uih_window *w = uih->wtop;
    struct image *img = uih->image;
    int size = 0;
    int nocopy = 0;
    int savedline = 0;
    int savedpos = 0;
    int destwidth = uih->image->width * uih->image->bytesperpixel;
    if (!uih->image->bytesperpixel) {
        destwidth = (w->x + uih->image->width + 7) / 8;
    }
    if (uih->wdisplayed)
        return;
    uih->wdisplayed = 1;
    while (w) {
        if (w->getpos != NULL) {
            int test = w->x == -65536;
            w->getpos (uih, &w->x, &w->y, &w->width, &w->height, w->data);
            if (w->x < 0)
                w->width -= w->x, w->x = 0;
            if (w->y < 0)
                w->height -= w->y, w->y = 0;
            if (w->x > img->width)
                w->width = 0, w->height = 0, w->x = 0;
            if (w->y > img->height)
                w->width = 0, w->height = 0, w->y = 0;
            if (w->x + w->width > img->width)
                w->width = img->width - w->x;
            if (w->y + w->height > img->height)
                w->height = img->height - w->y;
            if (w->width < 0)
                w->width = 0;
            if (w->height < 0)
                w->height = 0;
            if (test) {
                struct uih_window *w1 = uih->wtop;
                while (w1) {
                    if (w != w1 && (w1->flags & DRAWBORDER) && ((((w1->x > w->x + 5 && w1->x + 5 < w->x + w->width) || (w->x > w1->x + 5 && w->x + 5 < w1->x + w1->width)) && ((w1->y > w->y + 5 && w1->y + 5 < w->y + w->height) || (w->y > w1->y + 5 && w->y + 5 < w1->y + w1->height))) || (((w1->x + w1->width > w->x + 5 && w1->x + w1->width + 5 < w->x + w->width) || (w->x + w->width > w1->x + 5 && w->x + w->width + 5 < w1->x + w1->width)) && ((w1->y + w1->height > w->y + 5 && w1->y + w1->height + 5 < w->y + w->height) || (w->y + w->height > w1->y + 5 && w->y + w->height + 5 < w1->y + w1->height))))) {
                        w->flags &= ~BORDER_TRANSPARENT;
                        break;
                    }
                    w1 = w1->next;
                }
            }
            size += w->width * w->height;
            if (w->x == 0 && w->y == 0 && w->width == img->width && w->height == img->height)
                nocopy = 1;
            assert (w->width >= 0);
            assert (w->height >= 0);
            assert (w->x >= 0);
            assert (w->y >= 0);
            assert (w->x + w->width <= uih->image->width);
            assert (w->y + w->height <= uih->image->height);
        }
        w = w->next;
    }
    if (size > img->width * img->height / 2) {
        int i;
        int width = img->width * img->bytesperpixel;
        if (!width)
            width = (img->width + 7) / 8;
        uih->wflipped = 1;
        if (!nocopy)
            for (i = 0; i < img->height; i++)
                memcpy (img->oldlines[i], img->currlines[i], width);
        uih->image->flip (uih->image);
    } else {
        int savedminx = -1;
        int savedmaxx = -1;
        int savedminy = -1;
        int savedmaxy = -1;
        uih->wflipped = 0;
        w = uih->wtop;
        while (w) {
            int i;
            assert (w->saveddata == NULL);
            if (w->getpos == NULL) {
                if ((w->x < savedminx || w->y < savedminy || w->x + w->width > savedmaxx || w->x + w->height > savedmaxy || w->x + w->width < savedminx || w->y + w->height < savedminy || w->x > savedmaxx || w->y > savedmaxy)) {
                    w->saveddata = xsaveline (uih->image, w->x, w->y, w->width + w->x, w->height + w->y);
                }
            } else {
                assert (w->savedline == -1);
                if (w->width && w->height && (w->x < savedminx || w->y < savedminy || w->x + w->width > savedmaxx || w->y + w->height > savedmaxy)) {
                    int xskip = w->x * uih->image->bytesperpixel;
                    int width = w->width * uih->image->bytesperpixel;
                    savedminx = w->x;
                    savedminy = w->y;
                    savedmaxx = w->x + w->width;
                    savedmaxy = w->y + w->height;
                    if (!uih->image->bytesperpixel) {
                        xskip = w->x / 8;
                        width = (w->x + w->width + 7) / 8 - xskip;
                    }
                    if (uih->image->flags & PROTECTBUFFERS || 1) {
                        w->saveddata = (char *) malloc (width * w->height + 1);
                        if (w->saveddata != NULL)
                            for (i = w->y; i < w->y + w->height; i++) {
                                unsigned char *data = img->currlines[i] + xskip;
                                memcpy (w->saveddata + (i - w->y) * width, data, width);
                            }

                    } else {
                        w->savedline = savedline;
                        w->savedpos = savedpos;
                        for (i = w->y; i < w->y + w->height; i++) {
                            unsigned char *data = img->currlines[i] + xskip;
                            if (width + savedpos > destwidth) {
                                int width1;
                                memcpy (uih->image->oldlines[savedline] + savedpos, data, destwidth - savedpos);
                                savedline++;
                                width1 = width - destwidth + savedpos;
                                memcpy (uih->image->oldlines[savedline], data + (destwidth - savedpos), width1);
                                savedpos = width1;
                            } else
                                memcpy (uih->image->oldlines[savedline] + savedpos, data, width), savedpos += width;
                        }
                    }
                }
            }
            w = w->next;
        }
    }
    w = uih->wtop;
    while (w) {
        if (w->getpos == NULL) {
// This code unfortunately won't work for the Julia show, so
// deactivated:
#if 0
            int lw = 0, lwi = 0, lwj = 0;
            if (uih->image->width > 800 && uih->image->height > 600)
                lw = 3;

            for (; lwi <= lw; ++lwi)
                for (; lwj <= lw; ++lwj)
#endif
#define lwi 0
#define lwj 0
                    xline (uih->image, w->x + lwi, w->y + lwj, w->width + w->x + lwi, w->height + w->y + lwj, w->savedline);
        } else if (w->width && w->height) {
            if (w->flags & DRAWBORDER)
                uih_drawborder (uih, w->x, w->y, w->width, w->height, (BORDER_TRANSPARENT) & w->flags);
            w->draw (uih, w->data);
        }
        w = w->next;
    }
}
