/*
 *   MediaMVP Server
 *
 *   (C) 2004 Dominic Morris
 *
 *   $Id: graphics.c,v 1.5 2004/09/26 15:47:33 dom Exp $
 *   $Date: 2004/09/26 15:47:33 $
 *
 *
 *   Simple graphic routines - rectangles, ellipses and lines
 * 
 */

#include "libmvp_internal.h"

/* Beware of double evaluation */
#define max(a,b) a >= b ? a : b
#define min(a,b) a <= b ? a : b

/** \brief Draw a rectangle on the surface
 *
 *  \param sfc Surface to use
 *  \param x1 Top left corner
 *  \param y1 Top left corner
 *  \param x2 Bottom right corner
 *  \param y2 Bottom right corenr
 *  \param colour The colour to draw the rectangle
 *
 *  \note Code lifted from VDR http://www.cadsoft.de/vdr
 */
void surface_draw_rectangle(surface_t *sfc, int x1, int y1, int x2, int y2, uint32_t colour)
{
    int      x,y;

    x1 = max(x1,0);
    y1 = max(y1,0);
    x2 = min(x2, surface_get_width(sfc));
    y2 = min(y2, surface_get_height(sfc));

    for ( y = y1; y <= y2 ; y++ ) {
        for ( x = x1; x <= x2 ; x++ ) {
            surface_set_pixel(sfc,x,y,colour);
        }
    }
}

/** \brief Draws a filled ellipse
 *  \param x1 Top left
 *  \param y1 Top left
 *  \param x2 Lower right
 *  \param y2 Lower right
 *  \param colour 
 *  \param Quadrants Quadrants controls which parts of the ellipse are 
 *  actually drawn:
 *  0       draws the entire ellipse
 *  1..4    draws only the first, second, third or fourth quadrant, respectively
 *  5..8    draws the right, top, left or bottom half, respectively
 *  -1..-8  draws the inverted part of the given quadrant(s)
 *  If Quadrants is not 0, the coordinates are those of the actual area,  not
 *  the full circle
 *
 *  \note Code lifted from vdr (http://www.cadsoft.de/vdr)
 */
void surface_draw_ellipse(surface_t *sfc, int x1, int y1, int x2, int y2, uint32_t colour, int Quadrants)
{
// Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
    int TwoASquare, TwoBSquare;
    int x,y;
    int XChange, YChange;
    int EllipseError;
    int StoppingX, StoppingY;
    int rx = x2 - x1;
    int ry = y2 - y1;
    int cx = (x1 + x2) / 2;
    int cy = (y1 + y2) / 2;
    switch (abs(Quadrants)) {
    case 0: rx /= 2; ry /= 2; break;
    case 1: cx = x1; cy = y2; break;
    case 2: cx = x2; cy = y2; break;
    case 3: cx = x2; cy = y1; break;
    case 4: cx = x1; cy = y1; break;
    case 5: cx = x1;          ry /= 2; break;
    case 6:          cy = y2; rx /= 2; break;
    case 7: cx = x2;          ry /= 2; break;
    case 8:          cy = y1; rx /= 2; break;
    }
    TwoASquare = 2 * rx * rx;
    TwoBSquare = 2 * ry * ry;
    x = rx;
    y = 0;
    XChange = ry * ry * (1 - 2 * rx);
    YChange = rx * rx;
    EllipseError = 0;
    StoppingX = TwoBSquare * rx;
    StoppingY = 0;
    while (StoppingX >= StoppingY) {
        switch (Quadrants) {
        case  5: surface_draw_rectangle(sfc,cx,     cy + y, cx + x, cy + y, colour); // no break
        case  1: surface_draw_rectangle(sfc,cx,     cy - y, cx + x, cy - y, colour); break;
        case  7: surface_draw_rectangle(sfc,cx - x, cy + y, cx,     cy + y, colour); // no break
        case  2: surface_draw_rectangle(sfc,cx - x, cy - y, cx,     cy - y, colour); break;
        case  3: surface_draw_rectangle(sfc,cx - x, cy + y, cx,     cy + y, colour); break;
        case  4: surface_draw_rectangle(sfc,cx,     cy + y, cx + x, cy + y, colour); break;
        case  0:
        case  6: surface_draw_rectangle(sfc,cx - x, cy - y, cx + x, cy - y, colour); if (Quadrants == 6) break;
        case  8: surface_draw_rectangle(sfc,cx - x, cy + y, cx + x, cy + y, colour); break;
        case -1: surface_draw_rectangle(sfc,cx + x, cy - y, x2,     cy - y, colour); break;
        case -2: surface_draw_rectangle(sfc,x1,     cy - y, cx - x, cy - y, colour); break;
        case -3: surface_draw_rectangle(sfc,x1,     cy + y, cx - x, cy + y, colour); break;
        case -4: surface_draw_rectangle(sfc,cx + x, cy + y, x2,     cy + y, colour); break;
        }
        y++;
        StoppingY += TwoASquare;
        EllipseError += YChange;
        YChange += TwoASquare;
        if (2 * EllipseError + XChange > 0) {
            x--;
            StoppingX -= TwoBSquare;
            EllipseError += XChange;
            XChange += TwoBSquare;
        }
    }
    x = 0;
    y = ry;
    XChange = ry * ry;
    YChange = rx * rx * (1 - 2 * ry);
    EllipseError = 0;
    StoppingX = 0;
    StoppingY = TwoASquare * ry;
    while (StoppingX <= StoppingY) {
        switch (Quadrants) {
        case  5: surface_draw_rectangle(sfc,cx,     cy + y, cx + x, cy + y, colour); // no break
        case  1: surface_draw_rectangle(sfc,cx,     cy - y, cx + x, cy - y, colour); break;
        case  7: surface_draw_rectangle(sfc,cx - x, cy + y, cx,     cy + y, colour); // no break
        case  2: surface_draw_rectangle(sfc,cx - x, cy - y, cx,     cy - y, colour); break;
        case  3: surface_draw_rectangle(sfc,cx - x, cy + y, cx,     cy + y, colour); break;
        case  4: surface_draw_rectangle(sfc,cx,     cy + y, cx + x, cy + y, colour); break;
        case  0:
        case  6: surface_draw_rectangle(sfc,cx - x, cy - y, cx + x, cy - y, colour); if (Quadrants == 6) break;
        case  8: surface_draw_rectangle(sfc,cx - x, cy + y, cx + x, cy + y, colour); break;
        case -1: surface_draw_rectangle(sfc,cx + x, cy - y, x2,     cy - y, colour); break;
        case -2: surface_draw_rectangle(sfc,x1,     cy - y, cx - x, cy - y, colour); break;
        case -3: surface_draw_rectangle(sfc,x1,     cy + y, cx - x, cy + y, colour); break;
        case -4: surface_draw_rectangle(sfc,cx + x, cy + y, x2,     cy + y, colour); break;
        }
        x++;
        StoppingX += TwoBSquare;
        EllipseError += XChange;
        XChange += TwoBSquare;
        if (2 * EllipseError + YChange > 0) {
            y--;
            StoppingY -= TwoASquare;
            EllipseError += YChange;
            YChange += TwoASquare;
        }
    }
}

/** \brief Draw a line
 *  \param sfc Surface to use
 *  \param x0 Starting
 *  \param y0 Starting
 *  \param x1 Ending
 *  \param x2 Ending
 *  \param colour Colour
 *
 *  Taken from http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm_C_code
 */
void surface_draw_line(surface_t *sfc, int x0, int y0, int x1, int y1, uint32_t colour) 
{
    int i;
    int sx, sy;  /* step positive or negative (1 or -1) */
    int dx, dy;  /* delta (difference in X and Y between points) */
    int dx2, dy2;
    int e;
    int temp;

    dx = x1 - x0;
    sx = (dx > 0) ? 1 : -1;
    if (dx < 0)
        dx = -dx;

    dy = y1 - y0;
    sy = (dy > 0) ? 1 : -1;
    if (dy < 0)
        dy = -dy;

    dx2 = dx << 1; /* dx2 = 2 * dx */
    dy2 = dy << 1; /* dy2 = 2 * dy */

    if (dy <= dx) { /* steep */
        e = dy2 - dx;

        for (i = 0; i < dx; ++i) {
            surface_set_pixel(sfc,y0, x0, colour);

            while (e >= 0) {
                y0 += sy;
                e -= dx2;
            }

            x0 += sx;
            e += dy2;
        }
    } 
    else {
        /* swap x0 <-> y0 */
        temp = x0;
        x0 = y0;
        y0 = temp;

        /* swap dx <-> dy */
        temp = dx;
        dx = dy;
        dy = temp;

        /* swap dx2 <-> dy2 */
        temp = dx2;
        dx2 = dy2;
        dy2 = temp;

        /* swap sx <-> sy */
        temp = sx;
        sx = sy;
        sy = temp;

        e = dy2 - dx;

        for (i = 0; i < dx; ++i) {
            surface_set_pixel(sfc,y0, x0, colour);
            while (e >= 0) {
                y0 += sy;
                e -= dx2;
            }
            x0 += sx;
            e += dy2;
        }
    }

}


void surface_draw_round_rect(surface_t *sfc, int x, int y, int w, int h, int r, int t, uint32_t c, uint32_t b)
{
    if ( r > w/2 ) {
        r = w/2;
    }
    if ( r > h/2 ) {
        r = h/2;
    }

    surface_draw_rectangle(sfc,x +r , y + t + 1, x + w -r + 1, y +h - t,b);
    surface_draw_rectangle(sfc,x + t , y + r + t, x + w - t, y + h -r -t,b);
    surface_draw_ellipse(sfc,x +w -r -t ,y + h - r - t,x + w  , y + h,c,4);
    surface_draw_ellipse(sfc,x +w -r - t,y + h - r - t,x + w - t , y + h - t ,b,4);
    surface_draw_ellipse(sfc,x +w-r -t,y,x + w ,y + t + r,c,1);
    surface_draw_ellipse(sfc,x +w-r-t,y + t + 1,x + w - t, y + t +  r ,b,1);
    surface_draw_ellipse(sfc,x ,y + h - r - t  , x + r + t, y + h, c,3);
    surface_draw_ellipse(sfc,x + t ,y + h - r - t  , x + r + t, y + h - t, b,3);
    surface_draw_ellipse(sfc,x ,y ,x +r + t  , y + t + r   ,c,2);
    surface_draw_ellipse(sfc,x + t ,y + t ,x +r + t  , y  + r + t   ,b,2);

    surface_draw_rectangle(sfc,x+r,y,x + w-r, y +t   ,c);
    surface_draw_rectangle(sfc,x+r, y + h - t, x +w -r, y + h ,c);
    surface_draw_rectangle(sfc,x, y + t +  r, x + t - 1, y + h -r , c);
    surface_draw_rectangle(sfc,x + w - t + 1, y + t +  r, x + w , y + h -r , c);
}
