#include <windows.h>
#include "drawpie.h"

#ifdef  WIN32
#define MoveTo(_hdc,_x,_y) MoveToEx(_hdc, _x, _y, NULL)
#endif  // WIN32


int NEAR IntSqrt(unsigned long dwNum)
{
    // We will keep shifting dwNum left and look at the top two bits.

    // initialize sqrt and remainder to 0.
    DWORD dwSqrt = 0, dwRemain = 0, dwTry;
    int i;

    // We iterate 16 times, once for each pair of bits.
    for (i=0; i<16; ++i)
    {
        // Mask off the top two bits of dwNum and rotate them into the
        // bottom of the remainder
        dwRemain = (dwRemain<<2) | (dwNum>>30);

        // Now we shift the sqrt left; next we'll determine whether the
        // new bit is a 1 or a 0.
        dwSqrt <<= 1;

        // This is where we double what we already have, and try a 1 in
        // the lowest bit.
        dwTry = dwSqrt*2 + 1;

        if (dwRemain >= dwTry)
        {
            // The remainder was big enough, so subtract dwTry from
            // the remainder and tack a 1 onto the sqrt.
            dwRemain -= dwTry;
            dwSqrt |= 0x01;
        }

        // Shift dwNum to the left by 2 so we can work on the next few
        // bits.
        dwNum <<= 2;
    }

    return(dwSqrt);
}



VOID NEAR DrawPie(HDC hDC, LPCRECT lprcItem, UINT uPctX10, BOOL TrueZr100,
          UINT uOffset, const COLORREF FAR *lpColors)
{
    int cx, cy, rx, ry, x, y;
    int uQPctX10;
    RECT rcItem;
    HRGN hEllRect = NULL;
    HRGN hEllipticRgn = NULL;
    HRGN hRectRgn = NULL;
    HBRUSH hBrush = NULL;
    HBRUSH hOldBrush;
    HPEN hPen = NULL;
    HPEN hOldPen = NULL;

    rcItem = *lprcItem;
    rcItem.left = lprcItem->left;
    rcItem.top = lprcItem->top;
    rcItem.right = lprcItem->right - rcItem.left;
    rcItem.bottom = lprcItem->bottom - rcItem.top - uOffset;

    rx = rcItem.right / 2;
    cx = rcItem.left + rx;
    ry = rcItem.bottom / 2;
    cy = rcItem.top + ry;
    if (rx<=10 || ry<=10)
    {
        return;
    }

    rcItem.right = rcItem.left+2*rx;
    rcItem.bottom = rcItem.top+2*ry;

    if (uPctX10 > 1000)
    {
        uPctX10 = 1000;
    }

    /* Translate to first quadrant of a Cartesian system
    */
    uQPctX10 = (uPctX10 % 500) - 250;
    if (uQPctX10 < 0)
    {
        uQPctX10 = -uQPctX10;
    }

    /* Calc x and y.  I am trying to make the area be the right percentage.
    ** I don't know how to calculate the area of a pie slice exactly, so I
    ** approximate it by using the triangle area instead.
    */
    if (uQPctX10 < 120)
    {
        x = IntSqrt(((DWORD)rx*(DWORD)rx*(DWORD)uQPctX10*(DWORD)uQPctX10)
            /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));

        y = IntSqrt(((DWORD)rx*(DWORD)rx-(DWORD)x*(DWORD)x)*(DWORD)ry*(DWORD)ry/((DWORD)rx*(DWORD)rx));
    }
    else
    {
        y = IntSqrt((DWORD)ry*(DWORD)ry*(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)
            /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));

        x = IntSqrt(((DWORD)ry*(DWORD)ry-(DWORD)y*(DWORD)y)*(DWORD)rx*(DWORD)rx/((DWORD)ry*(DWORD)ry));
    }

    /* Switch on the actual quadrant
    */
    switch (uPctX10 / 250)
    {
    case 1:
        y = -y;
        break;

    case 2:
        break;

    case 3:
        x = -x;
        break;

    default: // case 0 and case 4
        x = -x;
        y = -y;
        break;
    }

    /* Now adjust for the center.
    */
    x += cx;
    y += cy;

    /* Draw the shadows using regions (to reduce flicker).
    */
    hEllipticRgn = CreateEllipticRgnIndirect(&rcItem);
    if (hEllipticRgn == NULL) {
        goto Cleanup;
    }
    OffsetRgn(hEllipticRgn, 0, uOffset);
    hEllRect = CreateRectRgn(rcItem.left, cy, rcItem.right, cy+uOffset);
    if (hEllRect == NULL) {
        goto Cleanup;
    }
    hRectRgn = CreateRectRgn(0, 0, 0, 0);
    if (hRectRgn == NULL) {
        goto Cleanup;
    }
    CombineRgn(hRectRgn, hEllipticRgn, hEllRect, RGN_OR);
    OffsetRgn(hEllipticRgn, 0, -(int)uOffset);
    CombineRgn(hEllRect, hRectRgn, hEllipticRgn, RGN_DIFF);

    /* Always draw the whole area in the free shadow/
    */
    hBrush = CreateSolidBrush(lpColors[DP_FREESHADOW]);
    if (hBrush)
    {
        FillRgn(hDC, hEllRect, hBrush);
        DeleteObject(hBrush);
        hBrush = NULL;
    }

    /* Draw the used shadow only if the disk is at least half used.
    */
    if (uPctX10>500 && (hBrush=CreateSolidBrush(lpColors[DP_USEDSHADOW]))!=NULL)
    {
        DeleteObject(hRectRgn);
        hRectRgn = CreateRectRgn(x, cy, rcItem.right, lprcItem->bottom);
        if (hRectRgn == NULL) {
            goto Cleanup;
        }
        CombineRgn(hEllipticRgn, hEllRect, hRectRgn, RGN_AND);
        FillRgn(hDC, hEllipticRgn, hBrush);
        DeleteObject(hBrush);
        hBrush = NULL;
    }

    hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
    if (hPen == NULL) {
        goto Cleanup;
    }
    hOldPen = SelectObject(hDC, hPen);

    if((uPctX10 < 100) && (cy == y))
    {
        hBrush = CreateSolidBrush(lpColors[DP_FREECOLOR]);
        if (hBrush == NULL) {
            goto Cleanup;
        }
        hOldBrush = SelectObject(hDC, hBrush);
        if((TrueZr100 == FALSE) || (uPctX10 != 0))
        {
        Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
            rcItem.left, cy, x, y);
        }
        else
        {
        Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right,
                 rcItem.bottom);
        }
    }
    else if((uPctX10 > (1000 - 100)) && (cy == y))
    {
        hBrush = CreateSolidBrush(lpColors[DP_USEDCOLOR]);
        if (hBrush == NULL) {
            goto Cleanup;
        }
        hOldBrush = SelectObject(hDC, hBrush);
        if((TrueZr100 == FALSE) || (uPctX10 != 1000))
        {
        Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
            rcItem.left, cy, x, y);
        }
        else
        {
        Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right,
                 rcItem.bottom);
        }
    }
    else
    {
        hBrush = CreateSolidBrush(lpColors[DP_USEDCOLOR]);
        if (hBrush == NULL) {
            goto Cleanup;
        }
        hOldBrush = SelectObject(hDC, hBrush);

        Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
        SelectObject(hDC, hOldBrush);
        DeleteObject(hBrush);
        hBrush = NULL;

        hBrush = CreateSolidBrush(lpColors[DP_FREECOLOR]);
        if (hBrush == NULL) {
            goto Cleanup;
        }
        hOldBrush = SelectObject(hDC, hBrush);
        Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
            rcItem.left, cy, x, y);
    }
    SelectObject(hDC, hOldBrush);
    DeleteObject(hBrush);
    hBrush = NULL;

    /* Do not draw the lines if the %age is truely 0 or 100 (completely
    ** empty disk or completly full disk)
    */
    if((TrueZr100 == FALSE) || ((uPctX10 != 0) && (uPctX10 != 1000)))
    {
        Arc(hDC, rcItem.left, rcItem.top+uOffset, rcItem.right, rcItem.bottom+uOffset,
            rcItem.left, cy+uOffset, rcItem.right, cy+uOffset-1);
        MoveTo(hDC, rcItem.left, cy);
        LineTo(hDC, rcItem.left, cy+uOffset);
        MoveTo(hDC, rcItem.right-1, cy);
        LineTo(hDC, rcItem.right-1, cy+uOffset);

        if (uPctX10 > 500)
        {
            MoveTo(hDC, x, y);
            LineTo(hDC, x, y+uOffset);
        }
    }
    SelectObject(hDC, hOldPen);

Cleanup:

    if (hRectRgn) {
        DeleteObject(hRectRgn);
    }
    if (hEllipticRgn) {
        DeleteObject(hEllipticRgn);
    }
    if (hEllRect) {
        DeleteObject(hEllRect);
    }
    if (hPen) {
        DeleteObject(hPen);
    }
    if (hBrush) {
        DeleteObject(hBrush);
    }

    return;
}
