// Geometric Tools, LLC
// Copyright (c) 1998-2014
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
//
// File Version: 5.0.2 (2011/06/27)

#include "Wm5ApplicationPCH.h"
#include "Wm5WindowApplication2.h"
using namespace Wm5;

//----------------------------------------------------------------------------
WindowApplication2::WindowApplication2 (const char* windowTitle,
    int xPosition, int yPosition, int width, int height,
    const Float4& clearColor)
    :
    // Rows are required to be multiple of 4 bytes, so width is adjusted.
    WindowApplication(windowTitle, xPosition, yPosition, width-(width % 4),
        height, clearColor),
    mScreenWidth(0),
    mScreenHeight(0),
    mScreen(0),
    mClampToWindow(true),
    mDoFlip(false)
{
}
//----------------------------------------------------------------------------
WindowApplication2::~WindowApplication2 ()
{
}
//----------------------------------------------------------------------------
bool WindowApplication2::OnInitialize ()
{
    if (!WindowApplication::OnInitialize())
    {
        return false;
    }

    // The RGBA screen pixels.
    mScreenWidth = GetWidth();
    mScreenHeight = GetHeight();
    mScreen = new1<ColorRGB>(mScreenWidth*mScreenHeight);
    ClearScreen();
    return true;
}
//----------------------------------------------------------------------------
void WindowApplication2::OnTerminate ()
{
    delete1(mScreen);
    WindowApplication::OnTerminate();
}
//----------------------------------------------------------------------------
void WindowApplication2::OnDisplay ()
{
    if (mRenderer->PreDraw())
    {
        mRenderer->ClearBuffers();
        mRenderer->Draw((const unsigned char*)mScreen, mDoFlip);
        ScreenOverlay();
        mRenderer->PostDraw();
        mRenderer->DisplayColorBuffer();
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::OnResize (int width, int height)
{
    width = width - (width % 4);
    WindowApplication::OnResize(width, height);
    if (width*height <= 0)
    {
        return;
    }

    if (width != mScreenWidth || height != mScreenHeight)
    {
        delete1(mScreen);
        mScreenWidth = width;
        mScreenHeight = height;
        mScreen = new1<ColorRGB>(mScreenWidth*mScreenHeight);
        ClearScreen();
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::ScreenOverlay ()
{
    // Stub for derived classes.
}
//----------------------------------------------------------------------------
void WindowApplication2::ClearScreen ()
{
    unsigned char r = (unsigned char)(255.0f*mClearColor[0]);
    unsigned char g = (unsigned char)(255.0f*mClearColor[1]);
    unsigned char b = (unsigned char)(255.0f*mClearColor[2]);
    unsigned char a = (unsigned char)(255.0f*mClearColor[3]);
    ColorRGB color(r, g, b, a);
    ColorRGB* current = mScreen;
    const int imax = mWidth*mHeight;
    for (int i = 0; i < imax; ++i, ++current)
    {
        *current = color;
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::DoFlip (bool doFlip)
{
    mDoFlip = doFlip;
}
//----------------------------------------------------------------------------
bool& WindowApplication2::ClampToWindow ()
{
    return mClampToWindow;
}
//----------------------------------------------------------------------------
void WindowApplication2::SetPixel (int x, int y, ColorRGB color)
{
    if (mClampToWindow)
    {
        if (0 <= x && x < mWidth && 0 <= y && y < mHeight)
        {
            mScreen[x + mWidth*y] = color;
        }
    }
    else
    {
        mScreen[x + mWidth*y] = color;
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::SetThickPixel (int x, int y, int thick,
    ColorRGB color)
{
    for (int dy = -thick; dy <= thick; ++dy)
    {
        for (int dx = -thick; dx <= thick; ++dx)
        {
            SetPixel(x + dx, y + dy, color);
        }
    }
}
//----------------------------------------------------------------------------
WindowApplication2::ColorRGB WindowApplication2::GetPixel (int x, int y)
{
    if (mClampToWindow)
    {
        if (0 <= x && x < mWidth && 0 <= y && y < mHeight)
        {
            return mScreen[x + mWidth*y];
        }
        else
        {
            return ColorRGB(0, 0, 0, 0);
        }
    }
    else
    {
        return mScreen[x + mWidth*y];
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::DrawLine (int x0, int y0, int x1, int y1,
    ColorRGB color)
{
    int x = x0, y = y0;

    // The direction of the line segment.
    int dx = x1-x0, dy = y1-y0;

    // Increment or decrement depending on the direction of the line.
    int sx = (dx > 0 ? 1 : (dx < 0 ? -1 : 0));
    int sy = (dy > 0 ? 1 : (dy < 0 ? -1 : 0));

    // Decision parameters for pixel selection.
    if (dx < 0)
    {
        dx = -dx;
    }
    if (dy < 0)
    {
        dy = -dy;
    }
    int ax = 2*dx, ay = 2*dy;
    int decx, decy;

    // Determine the largest direction component and single-step using the
    // related variable.
    int maxValue = dx, var = 0;
    if (dy > maxValue)
    {
        var = 1;
    }

    // Traverse the line segment using Bresenham's algorithm.
    switch (var)
    {
    case 0:  // Single-step in the x-direction.
        decy = ay - dx;
        for (/**/; /**/; x += sx, decy += ay)
        {
            // Process the pixel.
            SetPixel(x, y, color);

            // Take the Bresenham step.
            if (x == x1)
            {
                break;
            }
            if (decy >= 0)
            {
                decy -= ax;
                y += sy;
            }
        }
        break;
    case 1:  // Single-step in the y-direction.
        decx = ax - dy;
        for (/**/; /**/; y += sy, decx += ax)
        {
            // Process the pixel.
            SetPixel(x, y, color);

            // Take the Bresenham step.
            if (y == y1)
            {
                break;
            }
            if (decx >= 0)
            {
                decx -= ay;
                x += sx;
            }
        }
        break;
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::DrawRectangle (int xMin, int yMin, int xMax,
    int yMax, ColorRGB color, bool solid)
{
    if (xMin >= mWidth || xMax < 0 || yMin >= mHeight || yMax < 0)
    {
        // rectangle not visible
        return;
    }

    int x, y;

    if (solid)
    {
        for (y = yMin; y <= yMax; ++y)
        {
            for (x = xMin; x <= xMax; ++x)
            {
                SetPixel(x, y, color);
            }
        }
    }
    else
    {
        for (x = xMin; x <= xMax; ++x)
        {
            SetPixel(x, yMin, color);
            SetPixel(x, yMax, color);
        }
        for (y = yMin + 1; y <= yMax - 1; ++y)
        {
            SetPixel(xMin, y, color);
            SetPixel(xMax, y, color);
        }
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::DrawCircle (int xCenter, int yCenter, int radius,
    ColorRGB color, bool solid)
{
    int x, y, dec;

    if (solid)
    {
        int xValue, yMin, yMax, i;
        for (x = 0, y = radius, dec = 3 - 2*radius; x <= y; ++x)
        {
            xValue = xCenter + x;
            yMin = yCenter - y;
            yMax = yCenter + y;
            for (i = yMin; i <= yMax; ++i)
            {
                SetPixel(xValue, i, color);
            }

            xValue = xCenter - x;
            for (i = yMin; i <= yMax; ++i)
            {
                SetPixel(xValue, i, color);
            }

            xValue = xCenter + y;
            yMin = yCenter - x;
            yMax = yCenter + x;
            for (i = yMin; i <= yMax; ++i)
            {
                SetPixel(xValue, i, color);
            }

            xValue = xCenter - y;
            for (i = yMin; i <= yMax; ++i)
            {
                SetPixel(xValue, i, color);
            }

            if (dec >= 0)
            {
                dec += -4*(y--) + 4;
            }
            dec += 4*x + 6;
        }
    }
    else
    {
        for (x = 0, y = radius, dec = 3-2*radius; x <= y; ++x)
        {
            SetPixel(xCenter + x, yCenter + y, color);
            SetPixel(xCenter + x, yCenter - y, color);
            SetPixel(xCenter - x, yCenter + y, color);
            SetPixel(xCenter - x, yCenter - y, color);
            SetPixel(xCenter + y, yCenter + x, color);
            SetPixel(xCenter + y, yCenter - x, color);
            SetPixel(xCenter - y, yCenter + x, color);
            SetPixel(xCenter - y, yCenter - x, color);

            if (dec >= 0)
            {
                dec += -4*(y--) + 4;
            }
            dec += 4*x + 6;
        }
    }
}
//----------------------------------------------------------------------------
void WindowApplication2::Fill (int x, int y, ColorRGB foreColor,
    ColorRGB backColor)
{
    // Allocate the maximum amount of space needed.  If you prefer less, you
    // need to modify this data structure to allow for dynamic reallocation
    // when it is needed.  An empty stack has top == -1.
    int xMax = mWidth, yMax = mHeight;
    int stackSize = xMax*yMax;
    int* xStack = new1<int>(stackSize);
    int* yStack = new1<int>(stackSize);

    // Push the seed point onto the stack if it has the background color.  All
    // points pushed onto stack have background color backColor.
    int top = 0;
    xStack[top] = x;
    yStack[top] = y;

    while (top >= 0)  // The stack is not empty.
    {
        // Read the top of the stack.  Do not pop it since we need to return
        // to this top value later to restart the fill in a different
        // direction.
        x = xStack[top];
        y = yStack[top];

        // Fill the pixel.
        SetPixel(x, y, foreColor);

        int xp1 = x+1;
        if (xp1 < xMax && GetPixel(xp1, y) == backColor)
        {
            // Push the pixel with the background color.
            top++;
            xStack[top] = xp1;
            yStack[top] = y;
            continue;
        }

        int xm1 = x-1;
        if (0 <= xm1 && GetPixel(xm1, y) == backColor)
        {
            // Push the pixel with the background color.
            top++;
            xStack[top] = xm1;
            yStack[top] = y;
            continue;
        }

        int yp1 = y+1;
        if (yp1 < yMax && GetPixel(x, yp1) == backColor)
        {
            // Push the pixel with the background color.
            top++;
            xStack[top] = x;
            yStack[top] = yp1;
            continue;
        }

        int ym1 = y-1;
        if (0 <= ym1 && GetPixel(x, ym1) == backColor)
        {
            // Push the pixel with the background color.
            top++;
            xStack[top] = x;
            yStack[top] = ym1;
            continue;
        }

        // We are done in all directions, so pop and return to search other
        // directions.
        top--;
    }

    delete1(xStack);
    delete1(yStack);
}
//----------------------------------------------------------------------------
