/* shapes.c */
#include <gui.h>
#include <shapes.h>
#include <assembly.h>
#include <omnistd.h>

boolean videoinit;
rgb **globalcolors;

point *mkpoint(int16 x, int16 y, int8 color) {
    point *p;
    int16 size;

    size = sizeof(struct s_point);
    p = (point *)alloc(size);
    if (!p)
        return (point *)0;
    
    p->x = x;
    p->y = y;
    p->color = color;

    return p;
}

line *mkline(point *pnt1, point *pnt2, int8 color, int16 thickness) {
    line *ln;
    int16 size;
    
    if (!pnt1 || !pnt2 || !thickness)
        return (line *)0;
    
    size = sizeof(struct s_line);
    ln = (line *)alloc(size);
    if (!ln)
        return (line *)0;
    
    ln->p1 = pnt1;
    ln->p2 = pnt2;
    ln->color = color;
    ln->thickness = thickness;

    return ln;
}

rectangle *mkrectangle(point *pnt1, point *pnt2, int8 fgcolor, int8 bgcolor,
    int16 thickness, boolean solid)
{
    rectangle *rct;
    int16 size;

    if (!pnt1 || !pnt2)
        return (rectangle *)0;

    size = sizeof(struct s_rectangle);
    rct = (rectangle *)alloc(size);
    if (!rct)
        return (rectangle *)0;
    
    rct->p1 = pnt1;
    rct->p2 = pnt2;
    rct->fgcolor = fgcolor;
    rct->bgcolor = bgcolor;
    rct->thickness = thickness;
    rct->flags = FlagNone;
    if (solid)
        rct->flags |= FlagSolid;
    
    return rct;
}

rgb *mkrgb(int8 red, int8 green, int8 blue) {
    rgb *p;
    int16 size;

    size = sizeof(struct s_rgb);
    p = (rgb *)alloc(size);
    if (!p)
        return (rgb *)0;
    zero($1 p, size);

    p->red = red;
    p->green = green;
    p->blue = blue;

    return p;
}

rgb **setpalettes() {
    int16 reg, n;
    rgb *color;
    int8 red, green, blue;
    int8 high, low;
    rgb **clrtable;
    int16 size;

    size = (sizeof(rgb*) * 256);
    clrtable = (rgb **)alloc(size);
    if (!clrtable)
        return (rgb **)0;
    zero($1 clrtable, size);

    for (reg=n=0; n<256; n++) {
        if (reg > 15)
            reg = 0;
        if (!reg)
            red = green = blue = 0;
        else if (reg == 15)
            red = green = blue = 0xff;
        else {
            high = (reg+1);
            red = green = blue = low = 0;
            red = green = blue = (high<<4);
        }

        color = mkrgb(red, green, blue);
        if (!color)
            return (rgb **)0;
        setpalette(n, color);

        clrtable[n] = color;
        reg++;
    }

    return clrtable;
}

void setpalette(int8 reg, rgb *color) {
    if (!color)
        return;

    xsetpalette($2 reg, color->red,
        color->green, color->blue);
    
    return;
}

void videomode(int8 mode) {
    if (mode > 0x9f)
        return;

    xvideomode(mode);
    videoinit = true;

    return;
}

int16 getmaxx() {
    return (MaxX-1);
}

int16 getmaxy() {
    return (MaxY-1);
}

boolean drawpoint(point *pnt) {
    int16 maxx, maxy;

    if (!videoinit)
        return false;

    if (!pnt)
        return false;

    maxx = getmaxx();
    maxy = getmaxy();

    if ((pnt->x > maxx) || (pnt->y > maxy))
        return false;

    return xdrawpoint(pnt->x, pnt->y, pnt->color);
}

boolean drawline(line *ln) {
    point *pnt;
    int16 startx, starty;
    int16 maxx, maxy;
    int16 maxx_, maxy_;
    int16 x, y;

    if (!videoinit)
        return false;
    if (!ln)
        return false;
    
    maxx_ = getmaxx();
    maxy_ = getmaxy();

    if (ln->p1->x > ln->p2->x)
        return false;
    if (ln->p1->y > ln->p2->y)
        return false;

    if (ln->p1->x == ln->p2->x) {
        starty = ln->p1->y;
        maxy = (ln->p2->y+ln->thickness);
        if (maxy > maxy_)
            maxy = maxy_;

        startx = ln->p1->x;
        maxx = (ln->p1->x + ln->thickness);
        if (maxx > maxx_)
            maxx = maxx_;

    }
    else if (ln->p1->y == ln->p2->y) {
        startx = ln->p1->x;
        maxx = (ln->p2->x+ln->thickness);
        if (maxx > maxx_)
            maxx = maxx_;

        starty = ln->p1->y;
        maxy = (ln->p1->y + ln->thickness);
        if (maxy > maxy_)
            maxy = maxy_;
    }
    else
        return false;

    save();
    for (x=startx; x<maxx; x++)
        for (y=starty; y<maxy; y++) {
            pnt = mkpoint(x, y, ln->color);
            if (pnt)
                drawpoint(pnt);
            load();
        }
    
    return true;
}

boolean drawrectangle(rectangle *rct) {
    point *topright, *bottomleft;
    point *topleft, *bottomright;
    line *top, *bottom, *left, *right;
    int16 maxx, maxy;

    if (!videoinit)
        return false;
    if (!rct)
        return false;

    maxx = getmaxx();
    maxy = getmaxy();
    
    if ((rct->p1->x > maxx) || (rct->p1->y > maxy))
        return false;
    if ((rct->p1->x >= rct->p2->x) || (rct->p1->y >= rct->p2->y))
        return false;

    topleft = rct->p1;
    bottomright = rct->p2;
    topright = mkpoint(bottomright->x, topleft->y, rct->fgcolor);
    bottomleft = mkpoint(topleft->x, bottomright->y, rct->fgcolor);
    if (!topright || !bottomleft)
        return false;
    
    top = mkline(topleft, topright, rct->fgcolor, rct->thickness);
    bottom = mkline(bottomleft, bottomright, rct->fgcolor, rct->thickness);
    left = mkline(topleft, bottomleft, rct->fgcolor, rct->thickness);
    right = mkline(topright, bottomright, rct->fgcolor, rct->thickness);

    if (!top || !bottom || !left || !right)
        return false;
    
    drawline(top);
    drawline(bottom);
    drawline(left);
    drawline(right);

    // (xx) solid bgcolor
    return true;
}
