/*
 * PROJECT:         ReactOS VGA display driver
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            drivers/video/displays/vga/objects/lineto.c
 * PURPOSE:         
 * PROGRAMMERS:     Copyright (C) 1998-2003 ReactOS Team
 */

#include <vgaddi.h>

/*
 * Draw a line from top-left to bottom-right
 */
static void FASTCALL
vgaNWtoSE(
    IN CLIPOBJ* Clip,
    IN BRUSHOBJ* Brush,
    IN LONG x,
    IN LONG y,
    IN LONG deltax,
    IN LONG deltay)
{
    int i;
    int error;
    BOOLEAN EnumMore;
    PRECTL ClipRect;
    RECT_ENUM RectEnum;
    ULONG Pixel = Brush->iSolidColor;
    LONG delta;

    CLIPOBJ_cEnumStart(Clip, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
    EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
    ClipRect = RectEnum.arcl;
    delta = max(deltax, deltay);
    i = 0;
    error = delta / 2;
    while (i < delta && (ClipRect < RectEnum.arcl + RectEnum.c || EnumMore))
    {
        while ((ClipRect < RectEnum.arcl + RectEnum.c /* there's still a current clip rect */
                && (ClipRect->bottom <= y             /* but it's above us */
                    || (ClipRect->top <= y && ClipRect->right <= x))) /* or to the left of us */
               || EnumMore)                           /* no current clip rect, but rects left */
        {
            /* Skip to the next clip rect */
            if (RectEnum.arcl + RectEnum.c <= ClipRect)
            {
                EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
                ClipRect = RectEnum.arcl;
            }
            else
            {
                ClipRect++;
            }
        }
        if ( ClipRect < RectEnum.arcl + RectEnum.c ) /* If there's no current clip rect we're done */
        {
            if (ClipRect->left <= x && ClipRect->top <= y)
                vgaPutPixel ( x, y, Pixel );
            if ( deltax < deltay )
            {
                y++;
                error += deltax;
                if ( error >= deltay )
                {
                    x++;
                    error -= deltay;
                }
            }
            else
            {
                x++;
                error += deltay;
                if ( error >= deltax )
                {
                    y++;
                    error -= deltax;
                }
            }
            i++;
        }
    }
}

static void FASTCALL
vgaSWtoNE(
    IN CLIPOBJ* Clip,
    IN BRUSHOBJ* Brush,
    IN LONG x,
    IN LONG y,
    IN LONG deltax,
    IN LONG deltay)
{
    int i;
    int error;
    BOOLEAN EnumMore;
    PRECTL ClipRect;
    RECT_ENUM RectEnum;
    ULONG Pixel = Brush->iSolidColor;
    LONG delta;

    CLIPOBJ_cEnumStart(Clip, FALSE, CT_RECTANGLES, CD_RIGHTUP, 0);
    EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
    ClipRect = RectEnum.arcl;
    delta = max(deltax, deltay);
    i = 0;
    error = delta / 2;
    while (i < delta && (ClipRect < RectEnum.arcl + RectEnum.c || EnumMore))
    {
        while ((ClipRect < RectEnum.arcl + RectEnum.c
                && (y < ClipRect->top
                    || (y < ClipRect->bottom && ClipRect->right <= x)))
               || EnumMore)
        {
            if (RectEnum.arcl + RectEnum.c <= ClipRect)
            {
                EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
                ClipRect = RectEnum.arcl;
            }
            else
            {
                ClipRect++;
            }
        }
        if (ClipRect < RectEnum.arcl + RectEnum.c)
        {
            if (ClipRect->left <= x && y < ClipRect->bottom)
                vgaPutPixel(x, y, Pixel);
            if (deltax < deltay)
            {
                y--;
                error = error + deltax;
                if (deltay <= error)
                {
                    x++;
                    error = error - deltay;
                }
            }
            else
            {
                x++;
                error = error + deltay;
                if (deltax <= error)
                {
                    y--;
                    error = error - deltax;
                }
            }
            i++;
        }
    }
}

static void FASTCALL
vgaNEtoSW(
    IN CLIPOBJ* Clip,
    IN BRUSHOBJ* Brush,
    IN LONG x,
    IN LONG y,
    IN LONG deltax,
    IN LONG deltay)
{
    int i;
    int error;
    BOOLEAN EnumMore;
    PRECTL ClipRect;
    RECT_ENUM RectEnum;
    ULONG Pixel = Brush->iSolidColor;
    LONG delta;

    CLIPOBJ_cEnumStart(Clip, FALSE, CT_RECTANGLES, CD_LEFTDOWN, 0);
    EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
    ClipRect = RectEnum.arcl;
    delta = max(deltax, deltay);
    i = 0;
    error = delta / 2;
    while (i < delta && (ClipRect < RectEnum.arcl + RectEnum.c || EnumMore))
    {
        while ((ClipRect < RectEnum.arcl + RectEnum.c
                && (ClipRect->bottom <= y
                    || (ClipRect->top <= y && x < ClipRect->left)))
               || EnumMore)
        {
            if (RectEnum.arcl + RectEnum.c <= ClipRect)
            {
                EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
                ClipRect = RectEnum.arcl;
            }
            else
            {
                ClipRect++;
            }
        }
        if (ClipRect < RectEnum.arcl + RectEnum.c)
        {
            if (x < ClipRect->right && ClipRect->top <= y)
                vgaPutPixel(x, y, Pixel);
            if (deltax < deltay)
            {
                y++;
                error = error + deltax;
                if (deltay <= error)
                {
                    x--;
                    error = error - deltay;
                }
            }
            else
            {
                x--;
                error = error + deltay;
                if (deltax <= error)
                {
                    y++;
                    error = error - deltax;
                }
            }
            i++;
        }
    }
}

static void FASTCALL
vgaSEtoNW(
    IN CLIPOBJ* Clip,
    IN BRUSHOBJ* Brush,
    IN LONG x,
    IN LONG y,
    IN LONG deltax,
    IN LONG deltay)
{
    int i;
    int error;
    BOOLEAN EnumMore;
    PRECTL ClipRect;
    RECT_ENUM RectEnum;
    ULONG Pixel = Brush->iSolidColor;
    LONG delta;

    CLIPOBJ_cEnumStart(Clip, FALSE, CT_RECTANGLES, CD_LEFTUP, 0);
    EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
    ClipRect = RectEnum.arcl;
    delta = max(deltax, deltay);
    i = 0;
    error = delta / 2;
    while (i < delta && (ClipRect < RectEnum.arcl + RectEnum.c || EnumMore))
    {
        while ((ClipRect < RectEnum.arcl + RectEnum.c
                && (y < ClipRect->top
                    || (y < ClipRect->bottom && x < ClipRect->left)))
               || EnumMore)
        {
            if (RectEnum.arcl + RectEnum.c <= ClipRect)
            {
                EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
                ClipRect = RectEnum.arcl;
            }
            else
            {
                ClipRect++;
            }
        }
        if (ClipRect < RectEnum.arcl + RectEnum.c)
        {
            if (x < ClipRect->right && y < ClipRect->bottom)
                vgaPutPixel(x, y, Pixel);
            if (deltax < deltay)
            {
                y--;
                error = error + deltax;
                if (deltay <= error)
                {
                    x--;
                    error = error - deltay;
                }
            }
            else
            {
                x--;
                error = error + deltay;
                if (deltax <= error)
                {
                    y--;
                    error = error - deltax;
                }
            }
            i++;
        }
    }
}

/*
 * FIXME: Use Mix to perform ROPs
 * FIXME: Non-solid Brush
 */
BOOL APIENTRY
DrvLineTo(
    IN SURFOBJ *DestObj,
    IN CLIPOBJ *Clip,
    IN BRUSHOBJ *Brush,
    IN LONG x1,
    IN LONG y1,
    IN LONG x2,
    IN LONG y2,
    IN RECTL *RectBounds,
    IN MIX mix)
{
    LONG x, y, deltax, deltay, xchange, ychange, hx, vy;
    ULONG i;
    ULONG Pixel = Brush->iSolidColor;
    RECT_ENUM RectEnum;
    BOOL EnumMore;

    x = x1;
    y = y1;
    deltax = x2 - x1;
    deltay = y2 - y1;

    if (deltax < 0)
    {
        xchange = -1;
        deltax = - deltax;
        hx = x2+1;
        //x--;
    }
    else
    {
        xchange = 1;
        hx = x1;
    }

    if (deltay < 0)
    {
        ychange = -1;
        deltay = - deltay;
        vy = y2+1;
        //y--;
    }
    else
    {
        ychange = 1;
        vy = y1;
    }

    if (y1 == y2)
    {
        CLIPOBJ_cEnumStart(Clip, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
        do
        {
            EnumMore = CLIPOBJ_bEnum(Clip, sizeof(RectEnum), (PVOID) &RectEnum);
            for (i = 0; i < RectEnum.c && RectEnum.arcl[i].top <= y1; i++)
            {
                if (y1 < RectEnum.arcl[i].bottom &&
                    RectEnum.arcl[i].left <= hx + deltax &&
                    hx < RectEnum.arcl[i].right)
                {
                    vgaHLine(max(hx, RectEnum.arcl[i].left), y1,
                             min(hx + deltax, RectEnum.arcl[i].right)
                             -max(hx, RectEnum.arcl[i].left), Pixel);
                }
            }
        } while (EnumMore);
    }
    else if (x1 == x2)
    {
        CLIPOBJ_cEnumStart(Clip, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
        do
        {
            EnumMore = CLIPOBJ_bEnum(Clip, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
            for (i = 0; i < RectEnum.c; i++)
            {
                if (RectEnum.arcl[i].left <= x1 &&
                    x1 < RectEnum.arcl[i].right &&
                    RectEnum.arcl[i].top <= vy + deltay &&
                    vy < RectEnum.arcl[i].bottom)
                {
                    vgaVLine(x1,
                        max(vy, RectEnum.arcl[i].top),
                        min(vy + deltay, RectEnum.arcl[i].bottom)
                        - max(vy, RectEnum.arcl[i].top),
                        Pixel);
                }
            }
        } while (EnumMore);
    }
    else
    {
        if (0 < xchange)
        {
            if (0 < ychange)
                vgaNWtoSE(Clip, Brush, x, y, deltax, deltay);
            else
                vgaSWtoNE(Clip, Brush, x, y, deltax, deltay);
        }
        else
        {
            if (0 < ychange)
                vgaNEtoSW(Clip, Brush, x, y, deltax, deltay);
            else
                vgaSEtoNW(Clip, Brush, x, y, deltax, deltay);
        }
    }

    return TRUE;
}

/* EOF */
