
#include <stdlib.h>

#include "_gfx.h"
#include "_types.h"

/* Enables or disables debug output */
#ifdef __DEBUG
#define __D(fmt, args...) fprintf(stderr, "Gfx Debug: " fmt, ## args)
#else
#define __D(fmt, args...)
#endif

#define __E(fmt, args...) fprintf(stderr, "Gfx Error: " fmt, ## args)

#define DEPTH_OFFSET 0x10           // Color difference for button borders.
#define BORDER_SIZE  5              // Size of button borders in pixels.

#define MIN 0
#define MAX 1

/* Maximum height of screen supported is 1024 pixels */
unsigned int limits[1024][2];

static __u16 color_convert(__u8 r, __u8 g, __u8 b)
{
    __u16 col;

    /* Scale the color values to a 565 16bit image */
    r = r * (1<<5) / (1<<8);
    g = g * (1<<6) / (1<<8);
    b = b * (1<<5) / (1<<8);

    col = b | (g << 5) | (r << 11);

    return col;
}

static void line_compare(int x0, int y0, int x1, int y1)
{
    int     dy = y1 - y0;
    int     dx = x1 - x0;
    int     stepx, stepy;

    if (dy < 0) {
        dy = -dy;
        stepy = -1;
    }
    else {
        stepy = 1;
    }
    if (dx < 0) {
        dx = -dx;
        stepx = -1;
    }
    else {
        stepx = 1;
    }
    dy <<= 1;
    dx <<= 1;

    if (x0 < limits[y0][MIN]) limits[y0][MIN] = x0;
    if (x0 > limits[y0][MAX]) limits[y0][MAX] = x0;

    if (dx > dy) {
        int     fraction = dy - (dx >> 1);

        while (x0 != x1) {
            if (fraction >= 0) {
                y0 += stepy;
                fraction -= dx;
            }
            x0 += stepx;
            fraction += dy;
            if (x0 < limits[y0][MIN]) limits[y0][MIN] = x0;
            if (x0 > limits[y0][MAX]) limits[y0][MAX] = x0;
        }
    }
    else {
        int     fraction = dx - (dy >> 1);

        while (y0 != y1) {
            if (fraction >= 0) {
                x0 += stepx;
                fraction -= dy;
            }
            y0 += stepy;
            fraction += dx;
            if (x0 < limits[y0][MIN]) limits[y0][MIN] = x0;
            if (x0 > limits[y0][MAX]) limits[y0][MAX] = x0;
        }
    }
}


static void fill_rectangle(int x, int y, int w, int h, __u16 col,
                           _simplescreen *scrp)
{
    __u16 *dst = scrp->bufp + y * scrp->w + x;
    int i, j;

    for (j=0; j<h; j++) {
        for (i=0; i<w; i++) {
            dst[i] = col;
        }
        dst += scrp->w;
    }
}

static void fill_triangle(int x1, int y1, int x2, int y2, int x3, int y3,
                          __u16 col, _simplescreen *scrp)
{
    int     maxy, miny;
    int     x, y;
    __u16  *pos;

    miny = y1;
    if (y2 < miny) miny = y2;
    if (y3 < miny) miny = y3;

    maxy = y1;
    if (y2 > maxy) maxy = y2;
    if (y3 > maxy) maxy = y3;

    /* Initialize the max and min to extreme values */
    for (y=miny; y<=maxy; y++) {
        limits[y][MIN] = 0xffffffff;
        limits[y][MAX] = 0;
    }

    /* Find max and min X values for all used Y values */
    line_compare(x1, y1, x2, y2);
    line_compare(x2, y2, x3, y3);
    line_compare(x3, y3, x1, y1);

    pos = (__u16 *) scrp->bufp + miny * scrp->w;

    /* Draw horisontally between max and min X and max and min Y */
    for (y=miny; y<=maxy; y++) {
        for (x=limits[y][MIN]; x<=limits[y][MAX]; x++) {
            pos[x] = col;
        }
        pos += scrp->w;
    }
}

void draw_rectangle(_simplescreen *scrp, int x, int y, int w, int h,
                    int r, int g, int b)
{
    __u16 col;

    col = color_convert(r, g, b);

    fill_rectangle(x, y, w, h, col, scrp);
}

void gfx_render_button(_simplebutton *btnp, _simplescreen *scrp,
                              int r, int g, int b)
{
    __u16 main_col;
    __u16 dark_col;
    __u16 light_col;
    int w = btnp->w;
    int h = btnp->h;
    int x = btnp->x;
    int y = btnp->y;
    int border = btnp->border;
    int dr = (r - DEPTH_OFFSET < 0) ? 0 : r - DEPTH_OFFSET;
    int dg = (g - DEPTH_OFFSET < 0) ? 0 : g - DEPTH_OFFSET;
    int db = (b - DEPTH_OFFSET < 0) ? 0 : b - DEPTH_OFFSET;
    int lr = (r + DEPTH_OFFSET > 255) ? 255 : r + DEPTH_OFFSET;
    int lg = (g + DEPTH_OFFSET > 255) ? 255 : g + DEPTH_OFFSET;
    int lb = (b + DEPTH_OFFSET > 255) ? 255 : b + DEPTH_OFFSET;

    main_col = color_convert(r, g, b);
    dark_col = color_convert(dr, dg, db);
    light_col = color_convert(lr, lg, lb);

    /* Draw the background of the button */
    fill_rectangle(x, y, w, h, main_col, scrp);

    /* Draw the button depth */
    fill_rectangle(x, y+h-border, w, border, dark_col, scrp);
    fill_rectangle(x, y, border, h, dark_col, scrp);

    fill_rectangle(x+border, y, w-border, border, light_col, scrp);
    fill_triangle(x, y, x+border, y+border, x+(border*2), y, light_col, scrp);
    fill_rectangle(x+w-border, y, border, h-border, light_col, scrp);
    fill_triangle(x+w, y+h, x+w-border, y+h-border, x+w, y+h-border,
                  light_col, scrp);
}

