//---------------------------------------------------------------------------
//  DrawHelp.cpp - flat drawing helper routines
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "DrawHelp.h"
#include "rgn.h"

#define cxRESIZE       (ClassicGetSystemMetrics(SM_CXEDGE)+ClassicGetSystemMetrics( SM_CXSIZEFRAME ))
#define cyRESIZE       (ClassicGetSystemMetrics(SM_CYEDGE)+ClassicGetSystemMetrics( SM_CYSIZEFRAME ))
#define cxRESIZEPAD    ClassicGetSystemMetrics(SM_CXVSCROLL)
#define cyRESIZEPAD    ClassicGetSystemMetrics(SM_CYHSCROLL)

//---------------------------------------------------------------------------
typedef WORD (* HITTESTRECTPROC)(LPCRECT, int, int, const POINT&, WORD);
WORD _HitTestRectCorner( HITTESTRECTPROC, HITTESTRECTPROC, LPCRECT, 
                         int, int, int, int, const POINT&, 
                         WORD, WORD, WORD, WORD );

//---------------------------------------------------------------------------
WORD _HitTestRectLeft( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return ((WORD)((pt.x <= (prc->left + cxMargin)) ? HTLEFT : wMiss));
}
//---------------------------------------------------------------------------
WORD _HitTestRectTop( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return ((WORD)((pt.y <= (prc->top + cyMargin)) ? HTTOP : wMiss));
}
//---------------------------------------------------------------------------
WORD _HitTestRectRight( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return ((WORD)((pt.x >= (prc->right - cxMargin)) ? HTRIGHT : wMiss));
}
//---------------------------------------------------------------------------
WORD _HitTestRectBottom( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return ((WORD)((pt.y >= (prc->bottom - cyMargin)) ? HTBOTTOM : wMiss));
}
//---------------------------------------------------------------------------
WORD _HitTestRectTopLeft( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return _HitTestRectCorner(
        _HitTestRectLeft, _HitTestRectTop, prc, 
        cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD,
        pt, HTTOPLEFT, HTLEFT, HTTOP, wMiss );
}
//---------------------------------------------------------------------------
WORD _HitTestRectTopRight( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return _HitTestRectCorner( 
        _HitTestRectRight, _HitTestRectTop, prc, 
        cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD,
        pt, HTTOPRIGHT, HTRIGHT, HTTOP, wMiss );
}
//---------------------------------------------------------------------------
WORD _HitTestRectBottomLeft( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return _HitTestRectCorner( 
        _HitTestRectLeft, _HitTestRectBottom, prc, 
        cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD,
        pt, HTBOTTOMLEFT, HTLEFT, HTBOTTOM, wMiss );
}
//---------------------------------------------------------------------------
WORD _HitTestRectBottomRight( 
    LPCRECT prc, int cxMargin, int cyMargin, const POINT& pt, WORD wMiss )
{
    return _HitTestRectCorner(
        _HitTestRectRight, _HitTestRectBottom, prc, 
        cxMargin, cyMargin, cxRESIZEPAD, cyRESIZEPAD,
        pt, HTBOTTOMRIGHT, HTRIGHT, HTBOTTOM, wMiss );
}
//---------------------------------------------------------------------------
WORD _HitTestRectCorner(
    HITTESTRECTPROC pfnX, HITTESTRECTPROC pfnY, 
    LPCRECT prc,                        // target rect
    int cxMargin, int cyMargin,         // width, height of resizing borders
    int cxMargin2, int cyMargin2,       // width, height of scrollbars
    const POINT& pt,                    // test point
    WORD wHitC, WORD wHitX, WORD wHitY, // winning hittest codes
    WORD wMiss )                        // losing hittest code
{
    WORD wRetX = pfnX( prc, cxMargin, cyMargin, pt, wMiss );
    WORD wRetY = pfnY( prc, cxMargin, cyMargin, pt, wMiss );

    if( wMiss != wRetX && wMiss != wRetY )
        return wHitC;

    if( wMiss != wRetX )
    {
        wMiss = wHitX;
        if( wMiss != pfnY( prc, cxMargin2, cyMargin2, pt, wMiss ) )
            return wHitC;
    }
    else if( wMiss != wRetY )
    {
        wMiss = wHitY;
        if( wMiss != pfnX( prc, cxMargin2, cyMargin2, pt, wMiss ) )
            return wHitC;
    }

    return wMiss;
}

//---------------------------------------------------------------------------
WORD HitTest9Grid( LPCRECT prc, const MARGINS& margins, const POINT& pt )
{
    ASSERT(PtInRect(prc,pt));

    WORD wHit =  HTCLIENT;

    //  test left side
    if( HTLEFT == _HitTestRectLeft( prc, margins.cxLeftWidth, 0, pt, wHit ) )
    {
        if( HTTOP == _HitTestRectTop( prc, 0, margins.cyTopHeight, pt, wHit ) )
            return HTTOPLEFT;
        if( HTBOTTOM == _HitTestRectBottom( prc, 0, margins.cyBottomHeight, pt, wHit ) )
            return HTBOTTOMLEFT;
        wHit = HTLEFT;
    }
    else //  test right side
    if( HTRIGHT == _HitTestRectRight( prc, margins.cxRightWidth, 0, pt, wHit ) )
    {
        if( HTTOP == _HitTestRectTop( prc, 0, margins.cyTopHeight, pt, wHit ) )
            return HTTOPRIGHT;
        if( HTBOTTOM == _HitTestRectBottom( prc, 0, margins.cyBottomHeight, pt, wHit ) )
            return HTBOTTOMRIGHT;
        wHit = HTRIGHT;
    }
    else //  test top
    if( HTTOP == _HitTestRectTop( prc, 0, margins.cyTopHeight, pt, wHit ) )
    {
        return HTTOP;
    }
    else //  test bottom
    if( HTBOTTOM == _HitTestRectBottom( prc, 0, margins.cyBottomHeight, pt, wHit ) )
    {
        return HTBOTTOM;
    }

    return wHit;
}

//---------------------------------------------------------------------------
WORD _HitTestResizingRect( DWORD dwHTFlags, LPCRECT prc, const POINT& pt, 
                           WORD w9GridHit, WORD wMiss )
{
    WORD wHit = wMiss;
    BOOL fTestLeft    = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_LEFT);
    BOOL fTestTop     = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_TOP);
    BOOL fTestRight   = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_RIGHT);
    BOOL fTestBottom  = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_BOTTOM);
    BOOL fTestCaption = TESTFLAG( dwHTFlags, HTTB_CAPTION );

    switch( w9GridHit )
    {
        case HTLEFT:
            if( fTestLeft )
            {
                //  first test for a hit in the corner resizing areas, respecting caller's option flags.
                if( (fTestTop    && (wHit = _HitTestRectTopLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTLEFT) ||
                    (fTestBottom && (wHit = _HitTestRectBottomLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTLEFT) )
                    break;
                //  failed corners, just test the resizing margin within the specified 9-grid hit seg.
                wHit = _HitTestRectLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            }
            break;
        case HTTOP:
            if( fTestCaption )
                wHit = wMiss = HTCAPTION;
            
            if( fTestTop )
            {
                //  first test for a hit in the corner resizing areas, respecting caller's option flags.
                if( (fTestLeft  && (wHit = _HitTestRectTopLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTTOP) ||
                    (fTestRight && (wHit = _HitTestRectTopRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTTOP) )
                    break;
                //  failed corners, just test the resizing margin within the specified 9-grid hit seg.
                wHit = _HitTestRectTop( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            }
            break;
        
        case HTRIGHT:
            if( fTestRight )
            {
                //  first test for a hit in the corner resizing areas, respecting caller's option flags.
                if( (fTestTop && (wHit = _HitTestRectTopRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTRIGHT) ||
                    (fTestBottom && (wHit = _HitTestRectBottomRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTRIGHT) )
                    break;
                //  failed corners, just test the resizing margin within the specified 9-grid hit seg.
                 wHit = _HitTestRectRight( prc, cxRESIZE, cyRESIZE, pt, wMiss );
                break;
            }
        
        case HTBOTTOM:
            if( fTestBottom )
            {
                //  first test for a hit in the corner resizing areas, respecting caller's option flags.
                if( (fTestLeft  && (wHit = _HitTestRectBottomLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTBOTTOM) ||
                    (fTestRight && (wHit = _HitTestRectBottomRight( prc, cxRESIZE, cyRESIZE, pt, wMiss )) != HTBOTTOM) )
                    break;
                //  failed corners, just test the resizing margin within the specified 9-grid hit seg.
                wHit = _HitTestRectBottom( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            }
            break;
        
        case HTTOPLEFT:
            if( fTestCaption )
                wHit = wMiss = HTCAPTION;
            //  first test for a resizing hit in the corner, and failing that, test the
            //  resizing margin on either side.
            if( fTestTop && fTestLeft )
                wHit = _HitTestRectTopLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestLeft )
                wHit = _HitTestRectLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss ); 
            else if( fTestTop )
                wHit = _HitTestRectTop( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            break;
        
        case HTTOPRIGHT:
            if( fTestCaption )
                wHit = wMiss = HTCAPTION;
            //  first test for a resizing hit in the corner, and failing that, test the
            //  resizing margin on either side.
            if( fTestTop && fTestRight )
                wHit = _HitTestRectTopRight( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestRight )
                wHit = _HitTestRectRight( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestTop )
                wHit = _HitTestRectTop( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            break;
        
        case HTBOTTOMLEFT:
            //  first test for a resizing hit in the corner, and failing that, test the
            //  resizing margin on either side.
            if( fTestBottom && fTestLeft )
                wHit = _HitTestRectBottomLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestLeft )
                wHit = _HitTestRectLeft( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestBottom )
                wHit = _HitTestRectBottom( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            break;
        
        case HTBOTTOMRIGHT:
            //  first test for a resizing hit in the corner, and failing that, test the
            //  resizing margin on either side.
            if( fTestBottom && fTestRight )
                wHit = _HitTestRectBottomRight( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestRight )
                wHit = _HitTestRectRight( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            else if( fTestBottom )
                wHit = _HitTestRectBottom( prc, cxRESIZE, cyRESIZE, pt, wMiss );
            break;
    }
    return wHit;
}

//---------------------------------------------------------------------------
WORD HitTestRect(DWORD dwHTFlags, LPCRECT prc, const MARGINS& margins, const POINT& pt )
{
    WORD wHit = HTNOWHERE;
    
    if( PtInRect( prc, pt ) )
    {
        wHit = HitTest9Grid( prc, margins, pt );

        if( HTCLIENT != wHit )
        {
            if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER) )
            {
                WORD wMiss = HTBORDER;
                wHit = _HitTestResizingRect( dwHTFlags, prc, pt, wHit, wMiss );
            }
            else if( TESTFLAG(dwHTFlags, HTTB_CAPTION|HTTB_FIXEDBORDER) )
            {
                switch( wHit )
                {
                    case HTTOP:
                    case HTTOPLEFT:
                    case HTTOPRIGHT:
                        wHit = (WORD)(TESTFLAG(dwHTFlags, HTTB_CAPTION) ? HTCAPTION : HTBORDER);
                        break;
                    default:
                        wHit = HTBORDER;
                }                
            }

        } // !HTCLIENT
    } // PtInRect

    return wHit;
}

//---------------------------------------------------------------------------
WORD _HitTestResizingTemplate( DWORD dwHTFlags, HRGN hrgn, const POINT& pt,
                               WORD w9GridHit, WORD wMiss )
{
    WORD wHit = wMiss;
    BOOL fTestLeft    = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_LEFT);
    BOOL fTestTop     = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_TOP);
    BOOL fTestRight   = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_RIGHT);
    BOOL fTestBottom  = TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_BOTTOM);
    BOOL fTestCaption = TESTFLAG( dwHTFlags, HTTB_CAPTION );
    BOOL fInsideRgn;

    switch( w9GridHit )
    {
        case HTLEFT:
            if( !fTestLeft )
            {
                return wMiss;
            }
            break;
        case HTTOP:
            if( fTestCaption )
                wMiss = HTCAPTION;

            if( !fTestTop )
            {
                return wMiss;
            }
            break;
        case HTRIGHT:
            if( !fTestRight )
            {
                return wMiss;
            }
            break;
        case HTBOTTOM:
            if( !fTestBottom )
            {
                return wMiss;
            }
            break;
        case HTTOPLEFT:
            if( fTestCaption )
                wMiss = HTCAPTION;

            if( !fTestTop || !fTestLeft )
            {
                return wMiss;
            }
            break;

        case HTTOPRIGHT:
            if( fTestCaption )
                wMiss = HTCAPTION;

            if( !fTestTop || !fTestRight )
            {
                return wMiss;
            }
            break;

        case HTBOTTOMLEFT:
            if( !fTestBottom || !fTestLeft )
            {
                return wMiss;
            }
            break;

        case HTBOTTOMRIGHT:
            if( !fTestBottom || !fTestRight )
            {
                return wMiss;
            }
            break;
    }

    fInsideRgn = PtInRegion(hrgn, pt.x, pt.y);

    if( fInsideRgn )
    {
        wHit = w9GridHit;
    }
    return wHit;
}

//---------------------------------------------------------------------------
WORD HitTestTemplate(DWORD dwHTFlags, LPCRECT prc, HRGN hrgn, const MARGINS& margins, const POINT& pt )
{
    WORD wHit = HTNOWHERE;
    
    if( PtInRect( prc, pt ) )
    {
        wHit = HitTest9Grid( prc, margins, pt );

        if( HTCLIENT != wHit )
        {
            if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER) )
            {
                WORD wMiss = HTBORDER;
                wHit = _HitTestResizingTemplate( dwHTFlags, hrgn, pt, wHit, wMiss );
            }
            else if( TESTFLAG(dwHTFlags, HTTB_CAPTION|HTTB_FIXEDBORDER) )
            {
                switch( wHit )
                {
                    case HTTOP:
                    case HTTOPLEFT:
                    case HTTOPRIGHT:
                        wHit = (WORD)(TESTFLAG(dwHTFlags, HTTB_CAPTION) ? HTCAPTION : HTBORDER);
                        break;
                    default:
                        wHit = HTBORDER;
                }
            }

        } // !HTCLIENT
    }
    return wHit;
}

//  --------------------------------------------------------------------------
//  FillRectClr
//
//  History:    2000-12-06  lmouton     borrowed from comctl32\v6\cutils.c
//---------------------------------------------------------------------------
void FillRectClr(HDC hdc, LPRECT prc, COLORREF clr)
{
    COLORREF clrSave = SetBkColor(hdc, clr);
    ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL);
    SetBkColor(hdc, clrSave);
}

//---------------------------------------------------------------------------
//  _DrawEdge
//
// Classic values are:
//   clrLight = 192 192 192
//   clrHighlight = 255 255 255
//   clrShadow = 128 128 128
//   clrDkShadow = 0 0 0
//   clrFill = 192 192 192
//
//  History:    2000-12-06  lmouton     borrowed from comctl32\v6\cutils.c, modified colors
//---------------------------------------------------------------------------
HRESULT _DrawEdge(HDC hdc, const RECT *pDestRect, UINT uEdge, UINT uFlags, 
    COLORREF clrLight, COLORREF clrHighlight, COLORREF clrShadow, COLORREF clrDkShadow, COLORREF clrFill,
    OUT RECT *pContentRect)
{
    if (hdc == NULL || pDestRect == NULL)
        return E_INVALIDARG;

    HRESULT hr = S_OK;

    RECT     rc, rcD;
    UINT     bdrType;
    COLORREF clrTL = 0;
    COLORREF clrBR = 0;

    // This is were we would adjust for high DPI if the new "BF_DPISCALE" flag is specified in uFlags.
    int      cxBorder = GetSystemMetrics(SM_CXBORDER);
    int      cyBorder = GetSystemMetrics(SM_CYBORDER);
    
    //
    // Enforce monochromicity and flatness
    //    

    // if (oemInfo.BitCount == 1)
    //    uFlags |= BF_MONO;
    if (uFlags & BF_MONO)
        uFlags |= BF_FLAT;    

    CopyRect(&rc, pDestRect);

    //
    // Draw the border segment(s), and calculate the remaining space as we
    // go.
    //
    bdrType = (uEdge & BDR_OUTER);
    if (bdrType)
    {
DrawBorder:
        //
        // Get colors.  Note the symmetry between raised outer, sunken inner and
        // sunken outer, raised inner.
        //

        if (uFlags & BF_FLAT)
        {
            if (uFlags & BF_MONO)
                clrBR = (bdrType & BDR_OUTER) ? clrDkShadow : clrHighlight;
            else
                clrBR = (bdrType & BDR_OUTER) ? clrShadow: clrFill;
            
            clrTL = clrBR;
        }
        else
        {
            // 5 == HILIGHT
            // 4 == LIGHT
            // 3 == FACE
            // 2 == SHADOW
            // 1 == DKSHADOW

            switch (bdrType)
            {
                // +2 above surface
                case BDR_RAISEDOUTER:           // 5 : 4
                    clrTL = ((uFlags & BF_SOFT) ? clrHighlight : clrLight);
                    clrBR = clrDkShadow;     // 1
                    break;

                // +1 above surface
                case BDR_RAISEDINNER:           // 4 : 5
                    clrTL = ((uFlags & BF_SOFT) ? clrLight : clrHighlight);
                    clrBR = clrShadow;       // 2
                    break;

                // -1 below surface
                case BDR_SUNKENOUTER:           // 1 : 2
                    clrTL = ((uFlags & BF_SOFT) ? clrDkShadow : clrShadow);
                    clrBR = clrHighlight;      // 5
                    break;

                // -2 below surface
                case BDR_SUNKENINNER:           // 2 : 1
                    clrTL = ((uFlags & BF_SOFT) ? clrShadow : clrDkShadow);
                    clrBR = clrLight;        // 4
                    break;

                default:
                    hr = E_INVALIDARG;
            }
        }

        if FAILED(hr)
        {
            return hr;
        }

        //
        // Draw the sides of the border.  NOTE THAT THE ALGORITHM FAVORS THE
        // BOTTOM AND RIGHT SIDES, since the light source is assumed to be top
        // left.  If we ever decide to let the user set the light source to a
        // particular corner, then change this algorithm.
        //
            
        // Bottom Right edges
        if (uFlags & (BF_RIGHT | BF_BOTTOM))
        {            
            // Right
            if (uFlags & BF_RIGHT)
            {       
                rc.right -= cxBorder;
                // PatBlt(hdc, rc.right, rc.top, g_cxBorder, rc.bottom - rc.top, PATCOPY);
                rcD.left = rc.right;
                rcD.right = rc.right + cxBorder;
                rcD.top = rc.top;
                rcD.bottom = rc.bottom;

                FillRectClr(hdc, &rcD, clrBR);
            }
            
            // Bottom
            if (uFlags & BF_BOTTOM)
            {
                rc.bottom -= cyBorder;
                // PatBlt(hdc, rc.left, rc.bottom, rc.right - rc.left, g_cyBorder, PATCOPY);
                rcD.left = rc.left;
                rcD.right = rc.right;
                rcD.top = rc.bottom;
                rcD.bottom = rc.bottom + cyBorder;

                FillRectClr(hdc, &rcD, clrBR);
            }
        }
        
        // Top Left edges
        if (uFlags & (BF_TOP | BF_LEFT))
        {
            // Left
            if (uFlags & BF_LEFT)
            {
                // PatBlt(hdc, rc.left, rc.top, g_cxBorder, rc.bottom - rc.top, PATCOPY);
                rc.left += cxBorder;

                rcD.left = rc.left - cxBorder;
                rcD.right = rc.left;
                rcD.top = rc.top;
                rcD.bottom = rc.bottom; 

                FillRectClr(hdc, &rcD, clrTL);
            }
            
            // Top
            if (uFlags & BF_TOP)
            {
                // PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, g_cyBorder, PATCOPY);
                rc.top += cyBorder;

                rcD.left = rc.left;
                rcD.right = rc.right;
                rcD.top = rc.top - cyBorder;
                rcD.bottom = rc.top;

                FillRectClr(hdc, &rcD, clrTL);
            }
        }
        
    }

    bdrType = (uEdge & BDR_INNER);
    if (bdrType)
    {
        //
        // Strip this so the next time through, bdrType will be 0.
        // Otherwise, we'll loop forever.
        //
        uEdge &= ~BDR_INNER;
        goto DrawBorder;
    }

    //
    // Fill the middle & clean up if asked
    //
    if (uFlags & BF_MIDDLE)    
        FillRectClr(hdc, &rc, (uFlags & BF_MONO) ? clrHighlight : clrFill);

    if ((uFlags & BF_ADJUST) && (pContentRect != NULL))
        CopyRect(pContentRect, &rc);

    return hr;
}