/*~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=
**
**    FILE:       POV.CPP
**    DATE:       3/31/97
**    PROJ:       ATLAS
**    PROG:       JKH
**    COMMENTS:   
**
**    DESCRIPTION: Window class for a 360 degree Point Of View control
**                    
**                    
**
**    NOTE:       There are some issues with using extern "C" in this file.
**                If you don't understand why they are there, you're not
**                alone.  For now, and probably for a while they will be
**                here though, because I can't get this file and others
**                that use these services to compile without them.
**                Unfortunately the dynamics of this project don't really
**                afford me the time at present to figure this out.
**                TODO: figure this out
**
**    HISTORY:
**    DATE        WHO            WHAT
**    ----        ---            ----
**    3/31/97     a-kirkh        Wrote it.
**    
**
**
**
** Copyright (C) Microsoft 1997.  All Rights Reserved.
**
**~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/
#include "cplsvr1.h"       // for ghInst
#include "dicputil.h"   // for MAX_POVS
#include "POV.H"           //This module's stuff. 
#include <malloc.h>  // for _alloca

#include "resrc1.h"

//static HWND hPOVWnd = NULL;
#define NUM_ARROW_POINTS    8
//static VERTICEINFO *paptVInfo;  
static const VERTICEINFO VInfo[] = {XARROWPOINT, YARROWPOINT, XARROWRIGHTOUT, YARROWRIGHTOUT, XARROWRIGHTIN, YARROWRIGHTIN,
    XARROWRIGHTBOTTOM, YARROWRIGHTBOTTOM, XARROWLEFTBOTTOM, YARROWLEFTBOTTOM, XARROWLEFTIN,
    YARROWLEFTIN, XARROWLEFTOUT, YARROWLEFTOUT, XARROWPOINT, YARROWPOINT};
static LPRECT prcOldRegionBox[MAX_POVS];
static LPRECT prcNewRegionBox[MAX_POVS];

#define  DEF_POV_POS -1

static double  degrees[MAX_POVS] = {DEF_POV_POS, DEF_POV_POS, DEF_POV_POS, DEF_POV_POS};

static BYTE   nPOV = MAX_POVS;
static HBRUSH hBrush[MAX_POVS];
static HRGN hRegion[MAX_POVS];

extern HINSTANCE ghInst;

void SetDegrees(BYTE nPov, short *nDegrees, HWND hPOVWnd)
{
    nPOV = nPov -= 1;

    LPPOINT paptPoints = (LPPOINT)_alloca(sizeof(POINT[NUM_ARROW_POINTS]));
    assert (paptPoints);

    // Create the proper brush for the axis!
    do {
        degrees[nPov] = (double)nDegrees[nPov] / DI_DEGREES; // if angle == 180, degrees comes in as 18000

        paptPoints[0].x = GETXCOORD(VInfo[0].y, VInfo[0].x, degrees[nPov]);
        paptPoints[0].y = GETYCOORD(VInfo[0].y, VInfo[0].x, degrees[nPov]);                    
        paptPoints[1].x = GETXCOORD(VInfo[1].y, VInfo[1].x, degrees[nPov]);
        paptPoints[1].y = GETYCOORD(VInfo[1].y, VInfo[1].x, degrees[nPov]);
        paptPoints[2].x = GETXCOORD(VInfo[2].y, VInfo[2].x, degrees[nPov]);
        paptPoints[2].y = GETYCOORD(VInfo[2].y, VInfo[2].x, degrees[nPov]);                    
        paptPoints[3].x = GETXCOORD(VInfo[3].y, VInfo[3].x, degrees[nPov]);
        paptPoints[3].y = GETYCOORD(VInfo[3].y, VInfo[3].x, degrees[nPov]);                    
        paptPoints[4].x = GETXCOORD(VInfo[4].y, VInfo[4].x, degrees[nPov]);
        paptPoints[4].y = GETYCOORD(VInfo[4].y, VInfo[4].x, degrees[nPov]);                    
        paptPoints[5].x = GETXCOORD(VInfo[5].y, VInfo[5].x, degrees[nPov]);
        paptPoints[5].y = GETYCOORD(VInfo[5].y, VInfo[5].x, degrees[nPov]);                    
        paptPoints[6].x = GETXCOORD(VInfo[6].y, VInfo[6].x, degrees[nPov]);
        paptPoints[6].y = GETYCOORD(VInfo[6].y, VInfo[6].x, degrees[nPov]);                    
        paptPoints[7].x = GETXCOORD(VInfo[7].y, VInfo[7].x, degrees[nPov]);
        paptPoints[7].y = GETYCOORD(VInfo[7].y, VInfo[7].x, degrees[nPov]);                    

        if(hRegion[nPov])
        {
            DeleteObject(hRegion[nPov]);
            hRegion[nPov]=NULL;
        }
        hRegion[nPov] = CreatePolygonRgn(paptPoints, NUM_ARROW_POINTS, WINDING);

        //hBrush[nPov] = CreateSolidBrush((nPov < 1) ? POV1_COLOUR : 
        //                                (nPov < 2) ? POV2_COLOUR : 
        //                                (nPov < 3) ? POV3_COLOUR : POV4_COLOUR); */

        //if (hRegion[nPov] && hBrush[nPov])
        //{
        //    GetRgnBox(hRegion[nPov], prcNewRegionBox[nPov]);
        //
        //    //RedrawWindow(hPOVWnd, NULL, NULL, RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_ERASE); 
        //    InvalidateRect(hPOVWnd, prcOldRegionBox[nPov], TRUE);
        //    InvalidateRect(hPOVWnd, prcNewRegionBox[nPov], TRUE);
        //}
        RECT R;
        GetClientRect(hPOVWnd,&R);

        POINT Pnt[2];
        Pnt[0].x=R.left;
        Pnt[0].y=R.top;
        Pnt[1].x=R.right;
        Pnt[1].y=R.bottom;
        MapWindowPoints(hPOVWnd,GetParent(hPOVWnd),Pnt,2);
        R.left=Pnt[0].x;
        R.top=Pnt[0].y;
        R.right=Pnt[1].x;
        R.bottom=Pnt[1].y;
        InvalidateRect(GetParent(hPOVWnd), &R, TRUE);
    
    } while( nPov-- );

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//    FUNCTION :  POVWndProc
//    REMARKS  :  The callback function for the POVHat Window.
//                    
//    PARAMS   :  The usual callback funcs for message handling
//
//    RETURNS  :  LRESULT - Depends on the message
//    CALLS    :  
//    NOTES    :
//                WM_PAINT - Just calls DrawControl
//
//                PM_MYJOYPOSCHANGED - This is a private (WM_USER) message that is
//                called whenever a change in the POV hat occurs.
//                
LRESULT CALLBACK POVWndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    switch( iMsg ) {

//      case WM_CREATE:
//          hPOVWnd = hWnd;
//          return FALSE;

//      case WM_DESTROY:
//         return FALSE;
    
    case WM_DESTROY:
        {
            BYTE nPov=nPOV;
            do
            {
                if(hRegion[nPov])
                {
                    DeleteObject(hRegion[nPov]);
                    hRegion[nPov]=NULL;
                }
            }while(nPov--);
        }
        return 0;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hDC = BeginPaint(hWnd, &ps);

            // 1) Get client size information
            SetMapMode(hDC, MM_TEXT);                              
            RECT rClient;
            GetClientRect(hWnd, &rClient);
            BYTE nSizeX = (BYTE)rClient.right>>1;
            BYTE nSizeY = (BYTE)rClient.bottom>>1;

            // 2) Load the hub bitmap and display it
            //PREFIX #WI226648. False positive. There is no leak. DeleteObject frees.
            HBITMAP hPOVHubBitmap = (HBITMAP)LoadImage(ghInst, MAKEINTRESOURCE(IDB_POVHUB), IMAGE_BITMAP, 0, 0, NULL);
            assert(hPOVHubBitmap);
            DrawBitmap(hDC, hPOVHubBitmap, nSizeX-8, nSizeY-8);
            DeleteObject(hPOVHubBitmap);

            // 3) Setup the window to use symmetrical units on a 1000 X 1000 cartesian grid
            SetMapMode(hDC, MM_ISOTROPIC);
            SetWindowExtEx  (hDC, 1000, 1000, NULL);
            SetViewportExtEx(hDC, nSizeX, -nSizeY, NULL); 
            SetViewportOrgEx(hDC, nSizeX,  nSizeY, NULL);

            // 4) Draw the circle upon which the arrow seems to rotate
            SelectObject(hDC, (HBRUSH)GetStockObject(NULL_BRUSH));

            HPEN hPenOld = (HPEN)SelectObject(hDC, (HGDIOBJ)GetStockObject(DC_PEN)); 
            SetDCPenColor( hDC, GetSysColor(COLOR_WINDOWTEXT) );

            Ellipse(hDC, -CIRCLERADIUS, CIRCLERADIUS, CIRCLERADIUS, -CIRCLERADIUS);
            SelectObject(hDC, hPenOld);

            // 5) Paint the Arrow at the correct angle if POV active
            BYTE nPov = nPOV;
            HBRUSH hBrushOld;

            do {
                if( degrees[nPov] >= 0 ) {
                    hBrush[nPov] = CreateSolidBrush((nPov < 1) ? POV1_COLOUR : 
                                                    (nPov < 2) ? POV2_COLOUR : 
                                                    (nPov < 3) ? POV3_COLOUR : POV4_COLOUR);                

                    hBrushOld = (HBRUSH)SelectObject(hDC, (HGDIOBJ)hBrush[nPov]); 



                    assert(hBrushOld);

                    PaintRgn(hDC, hRegion[nPov]);

                    // GetRgnBox returns zero if it fails...
                    GetRgnBox(hRegion[nPov], prcOldRegionBox[nPov]);
                    SelectObject(hDC, hBrushOld); 

                    if(hRegion[nPov])
                    {
                        DeleteObject(hRegion[nPov]);
                        hRegion[nPov]=NULL;
                    }
                    DeleteObject(hBrush[nPov] ); 
                }
            }   while( nPov-- );

            EndPaint(hWnd, &ps);
        }
        //PREFIX #WI226648. False positive. See above.
        return(0);

    default:
        return(DefWindowProc(hWnd, iMsg,wParam, lParam));
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//    FUNCTION :  RegisterPOVClass
//    REMARKS  :  Registers the POV Hat window.
//                    
//    PARAMS   :  hInstance - Used for the call to RegisterClassEx
//
//    RETURNS  :  TRUE - if successfully registered
//                FALSE - failed to register
//    CALLS    :  RegisterClassEx
//    NOTES    :
//

extern ATOM RegisterPOVClass()
{
    LPWNDCLASSEX pPOVWndClass    = (LPWNDCLASSEX)_alloca(sizeof(WNDCLASSEX));
    assert (pPOVWndClass);

    ZeroMemory(pPOVWndClass, sizeof(WNDCLASSEX));

    pPOVWndClass->cbSize        = sizeof(WNDCLASSEX);
    pPOVWndClass->style         = CS_HREDRAW; // | CS_VREDRAW;
    pPOVWndClass->lpfnWndProc   = POVWndProc;
    pPOVWndClass->hInstance     = ghInst;
    pPOVWndClass->hbrBackground = NULL;
    pPOVWndClass->lpszClassName = TEXT("POVHAT");

    return(RegisterClassEx( pPOVWndClass ));
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//    FUNCTION :  DrawBitmap
//    REMARKS  :  Copied verbatim from Petzold (WIN95 pg 190)
//    PARAMS   :  HDC - dc for drawing
//                HBITMAP - bitmap to draw
//                int xstart, ystart - where to place the bitmap
//
//    RETURNS  :  void
//    CALLS    :  
//    NOTES    :
//
void DrawBitmap(HDC hDC, HBITMAP hBitmap, BYTE xStart, BYTE yStart)
{
    HDC hdcMem = CreateCompatibleDC(hDC);

    // Found by prefix: Millen Bug129155. manbugs 29339
    // If CreateCompatibleDC fails, we should'nt proceed.
    if( hdcMem == NULL ) return;

    SelectObject(hdcMem, hBitmap);
    SetMapMode(hdcMem,GetMapMode(hDC));

    // Be aware!  This is the size of the current BITMAP...
    // IF IT CHANGES THIS WILL FAIL!!!
    POINT ptSize = {16, 16};
    DPtoLP(hDC, &ptSize, 1);

    POINT ptOrg = {0,0};
    DPtoLP(hdcMem, &ptOrg, 1);

    BitBlt(hDC, xStart, yStart, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, SRCAND);

    DeleteDC(hdcMem);
}

//~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=EOF=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=