/*
 * VisualBoyAdvanced - Nintendo Gameboy/GameboyAdvance (TM) emulator
 * Copyrigh(c) 1999-2002 Forgotten (vb@emuhq.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*----------------------------------------------------------------------
Copyright (c) 1998 Gipsysoft. All Rights Reserved.
Please see the file "licence.txt" for licencing details.
File:   WinHelper.h
Owner:  russf@gipsysoft.com
Purpose:        Windows helper functions, classes, structures and macros
                                        that make life a little easier
                                        These should all be zero impact classes etc. that is they 
                                        should *not* have a cpp file associated with them.
----------------------------------------------------------------------*/
#ifndef WINHELPER_H
#define WINHELPER_H

//#ifndef DEBUGHLP_H
//      #include <DebugHlp.h>
//#endif        //      DEBUGHLP_H

#ifndef FASTCALL
        #define FASTCALL
#endif  //      FASTCALL

extern void WndAssertFailed(char *, int, char *);
extern void WndApiFailure(char *, int, char *);

#define VERIFY(a) ASSERT(a)
#define ASSERT(a) \
  do {\
    if(!(a)) {\
      WndAssertFailed(__FILE__, __LINE__, #a);\
    }\
  } while(0);
 
#define VAPI(a) \
  do { \
    if(!(a)) {\
     WndApiFailure(__FILE__, __LINE__, #a); \
    }\
  } while (0);

#define ASSERT_VALID_HWND(a) ASSERT( ::IsWindow(a) )

namespace WinHelper
{

        class CSize : public tagSIZE
        //
        //      Wrapper for the SIZE structure
        {
        public:
                inline CSize() {};
                inline explicit CSize( const SIZE &size ) { cx = size.cx; cy = size.cy; }
                inline explicit CSize( long nSizeX, long nSizeY ) { cx = nSizeX; cy = nSizeY; }
                inline explicit CSize( const CSize& size ) { cx = size.cx; cy = size.cy; };
                inline void Set( long nSizeX, long nSizeY ) { cx = nSizeX; cy = nSizeY; }
                inline operator LPSIZE() { return this; };

                inline bool operator !=( const SIZE &size ) const { return cx != size.cx || cy != size.cy;}
                inline CSize & operator =( const SIZE &size ) { cx = size.cx; cy = size.cy; return *this; }
                inline void Empty() { cx = cy = 0; }
        };


        class CRect : public tagRECT
        //
        //      Wrapper for a RECT structure
        {
        public:
                inline CRect() {}
                //      Initialisation constructor
                inline explicit CRect( const RECT& rhs ) { Set( rhs.left, rhs.top, rhs.right, rhs.bottom );}
                inline CRect(int xLeft, int yTop, int xRight, int yBottom) { Set( xLeft, yTop, xRight, yBottom ); }
                //      Get the width of the rectangle
                inline int Width() const { return right - left; }
                //      Get the height of the rectangle
                inline int Height() const { return bottom - top; }
                //      overloaded operator so you don't have to do &rc anymore
                inline operator LPCRECT() const { return this; };
                inline operator LPRECT() { return this; };
                //      Return the SIZE of the rectangle;
                inline CSize Size() const { CSize s( Width(), Height() ); return s; }
                //      Return the top left of the rectangle
                inline POINT TopLeft() const { POINT pt = { left, top }; return pt; }   
                //      Return the bottom right of the rectangle
                inline POINT BottomRight() const { POINT pt = { right, bottom }; return pt; }   
                //      Set the rectangles left, top, right and bottom
                inline void Set( int xLeft, int yTop, int xRight, int yBottom) { top = yTop; bottom = yBottom; right = xRight; left = xLeft; }
                //      Return true if the rectangle contains all zeros
                inline bool IsEmpty() const { return left == 0 && right == 0 && top == 0 && bottom == 0 ? true : false; }
                //      Zero out our rectangle
                inline void Empty() { left = right = top = bottom = 0; }
                //      Set the size of the rect but leave the top left position untouched.
                inline void SetSize( const CSize &size ) { bottom = top + size.cy; right = left + size.cx; }
                inline void SetSize( const SIZE &size ) { bottom = top + size.cy; right = left + size.cx; }
                inline void SetSize( int cx, int cy ) { bottom = top + cy; right = left + cx; }
                //      Move the rectangle by an offset
                inline void Offset( int cx, int cy )
                        {
                                top+=cy;
                                bottom+=cy;
                                right+=cx;
                                left+=cx;
                        }
                //      Inflate the rectangle by the cx and cy, use negative to shrink the rectangle
                inline void Inflate( int cx, int cy )
                        {
                                top-=cy;
                                bottom+=cy;
                                right+=cx;
                                left-=cx;
                        }
                //      Assignment from a RECT
                inline CRect &operator = ( const RECT&rhs )
                        {
                                left = rhs.left; top = rhs.top;
                                right = rhs.right; bottom = rhs.bottom;
                                return *this;
                        }

                //      Return true if the point passed is within the rectangle
                inline bool PtInRect( const POINT &pt ) const   {       return  ( pt.x >= left && pt.x < right && pt.y >=top && pt.y < bottom ); }
                //      Return true if the rectangle passed overlaps this rectangle
                inline bool Intersect( const RECT &rc ) const { return ( rc.left < right && rc.right > left && rc.top < bottom && rc.bottom > top ); }
        };


        class CPoint : public tagPOINT
        //
        //      Wrapper for the POINT structure
        {
        public:
                inline CPoint() {};
                inline CPoint( LPARAM lParam ) { x = LOWORD( lParam ); y = HIWORD(lParam); }
                inline CPoint( int nX, int nY ) { x = nX; y = nY; }
                inline CPoint( const POINT &pt ) { x = pt.x; y = pt.y; }
                inline bool operator == ( const CPoint &rhs ) const { return x == rhs.x && y == rhs.y; }
                inline bool operator != ( const CPoint &rhs ) const { return x != rhs.x || y != rhs.y; }
                inline operator LPPOINT () { return this; }
        };


        class CScrollInfo : public tagSCROLLINFO
        {
        public:
                CScrollInfo( UINT fPassedMask ) { cbSize = sizeof( tagSCROLLINFO ); fMask = fPassedMask; }
        };


        class CCriticalSection
        //
        //      Simple crtical section handler/wrapper
        {
        public:
                inline CCriticalSection()       { ::InitializeCriticalSection(&m_sect); }
                inline ~CCriticalSection() { ::DeleteCriticalSection(&m_sect); }

                //      Blocking lock.
                inline void Lock()                      { ::EnterCriticalSection(&m_sect); }
                //      Unlock
                inline void Unlock()            { ::LeaveCriticalSection(&m_sect); }

                class CLock
                //
                //      Simple lock class for the critcal section
                {
                public:
                        inline CLock( CCriticalSection &sect ) : m_sect( sect ) { m_sect.Lock(); }
                        inline ~CLock() { m_sect.Unlock(); }
                private:
                        CCriticalSection &m_sect;

                        CLock();
                        CLock( const CLock &);
                        CLock& operator =( const CLock &);
                };

        private:
                CRITICAL_SECTION m_sect;

                CCriticalSection( const CCriticalSection & );
                CCriticalSection& operator =( const CCriticalSection & );
        };


        #define ZeroStructure( t ) ZeroMemory( &t, sizeof( t ) )
        #define countof( t )    (sizeof( (t) ) / sizeof( (t)[0] ) )
        #define UNREF(P) UNREFERENCED_PARAMETER(P)

        inline bool IsShiftPressed()
        {
                return GetKeyState(VK_SHIFT) & 0x8000 ? true : false;
        }

        inline bool IsAltPressed()
        {
                return GetKeyState(VK_MENU) & 0x8000 ? true : false;
        }

        inline bool IsControlPressed()
        {
                return GetKeyState(VK_CONTROL) & 0x8000 ? true : false;
        }

        inline HICON LoadIcon16x16( HINSTANCE hInst, UINT uID )
        //
        //      Load a 16x16 icon from the same resource as the other size icons.
        {
                return reinterpret_cast<HICON>( ::LoadImage( hInst, MAKEINTRESOURCE( uID ), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR ) );
        }


        class CDeferWindowPos
        //
        //      Wrapper for the Begin, Defer and End WindowPos functions. Nothing glamorous.
        {
        public:
                inline CDeferWindowPos( const int nWindows = 1 ) : m_hdlDef( ::BeginDeferWindowPos( nWindows ) ) {}
                inline ~CDeferWindowPos() { VERIFY( ::EndDeferWindowPos( m_hdlDef ) ); }
                inline HDWP DeferWindowPos( HWND hWnd, HWND hWndInsertAfter , int x, int y, int cx, int cy, UINT uFlags )
                {
                        return ::DeferWindowPos( m_hdlDef, hWnd, hWndInsertAfter, x, y, cx, cy, uFlags );
                }
                inline HDWP DeferWindowPos( HWND hWnd, HWND hWndInsertAfter, const CRect &rc, UINT uFlags )
                {
                        return ::DeferWindowPos( m_hdlDef, hWnd, hWndInsertAfter, rc.left, rc.top, rc.Width(), rc.Height(), uFlags );
                }

        private:
                HDWP m_hdlDef;
        };

}       //      WinHelper

#endif //WINHELPER_H
