#pragma once
#include <wtl/atlgdi.h>

class CTitleTip : public CWindowImpl< CTitleTip >
{
public:
	CTitleTip()
	{
		m_hWndParent = NULL;
	}
	
	~CTitleTip()
	{
	}
	
	DECLARE_WND_CLASS_EX( _T( "TitleTip" ), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_SAVEBITS, COLOR_WINDOW )

protected:
	HWND m_hWndParent;
	stdstr m_strToolTip;
	
	COLORREF m_rgbBackground;
	COLORREF m_rgbTextColour;
	COLORREF m_rgbBorderOuter;
	COLORREF m_rgbBorderInner;
	COLORREF m_rgbBackgroundTop;
	COLORREF m_rgbBackgroundBottom;
	
	CFont m_fntTitleFont;
	CToolTipCtrl m_ttToolTip;
	
public:
	BOOL Create( HWND hWndParent )
	{
		m_hWndParent = hWndParent;
		
		m_rgbBackground = GetSysColor( COLOR_INFOBK );
		m_rgbTextColour = GetSysColor( COLOR_INFOTEXT );		
		m_rgbBorderOuter = RGB( 220, 220, 220 );
		m_rgbBorderInner = RGB( 245, 245, 245 );
		m_rgbBackgroundTop = RGB( 250, 250, 250 );
		m_rgbBackgroundBottom = RGB( 235, 235, 235 );
		
		CRect Area(NULL);
		if ( CWindowImpl< CTitleTip >::Create( hWndParent, Area, NULL, WS_POPUP, WS_EX_TOOLWINDOW | WS_EX_TOPMOST ) == NULL )
			return FALSE;
		
		// create the tooltip
		if ( !m_ttToolTip.Create( m_hWnd ) )
			return FALSE;
		m_ttToolTip.SetMaxTipWidth( SHRT_MAX );
		
		// get system message font
		WTL::CLogFont logFont;
		logFont.SetMessageBoxFont();
		if ( !m_fntTitleFont.IsNull() )
			m_fntTitleFont.DeleteObject();
		return ( m_fntTitleFont.CreateFontIndirect( &logFont ) != NULL );
	}
	
	BOOL Show( CRect& rcRect, LPCTSTR lpszItemText, LPCTSTR lpszToolTip )
	{
		stdstr strItemText = lpszItemText;
		
		if ( !IsWindow() || strItemText.empty() )
			return FALSE;
		
		m_strToolTip = lpszToolTip;
		SetWindowText( strItemText.c_str() );
		
		CClientDC dcClient( m_hWnd );
		
		HFONT hOldFont = dcClient.SelectFont( m_fntTitleFont );
			
		CRect rcTextExtent( rcRect );
				
		// calculate item text extent...
		dcClient.DrawText( strItemText.c_str(), strItemText.length(), rcTextExtent, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_CALCRECT );
		
		dcClient.SelectFont( hOldFont );
		
		// do not show titletip if entire text is visible
		if ( rcTextExtent.Width() <= rcRect.Width() - 1 )
			return FALSE;
		
		if ( m_strToolTip.empty() )
			m_ttToolTip.Activate( FALSE );
		else
		{
			m_ttToolTip.Activate( TRUE );
			m_ttToolTip.AddTool( m_hWnd, (LPCTSTR)m_strToolTip. substr(0,SHRT_MAX).c_str() );
		}
		
		// show titletip at new location
		if ( !SetWindowPos( NULL, rcRect.left - 4, rcRect.top, rcTextExtent.Width() + 11, rcRect.Height(), SWP_NOZORDER | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS ) )
			return FALSE;
		
		SetCapture();
		
		return TRUE;
	}
	
	BOOL Hide()
	{
		if ( GetCapture() == m_hWnd )
			ReleaseCapture();
		return IsWindow() ? ShowWindow( SW_HIDE ) : FALSE;
	}
	
	BEGIN_MSG_MAP_EX(CTitleTip)
		MSG_WM_DESTROY(OnDestroy)
		MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST,WM_MOUSELAST,OnMouseRange)
		MSG_WM_ERASEBKGND(OnEraseBkgnd)
		MSG_WM_PAINT(OnPaint)
	END_MSG_MAP()
	
	void OnDestroy()
	{
		if ( m_ttToolTip.IsWindow() )
			m_ttToolTip.DestroyWindow();
		m_ttToolTip.m_hWnd = NULL;
	}
	
	LRESULT OnMouseRange( UINT nMessage, WPARAM wParam, LPARAM lParam )
	{
		SetMsgHandled( FALSE );
		
		if ( m_ttToolTip.IsWindow() )
		{
			MSG msgRelay = { m_hWnd, nMessage, wParam, lParam };
			m_ttToolTip.RelayEvent( &msgRelay );
		}
		
		CPoint ptMouse( GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
		ClientToScreen( &ptMouse );
		
		if ( nMessage == WM_MOUSEMOVE )
		{
			CRect rcWindow;
			GetWindowRect( rcWindow );		
			if ( !rcWindow.PtInRect( ptMouse ) )
				Hide();
			return 0;
		}		
										
		CWindow wndParent( m_hWndParent );
		UINT nHitTest = (UINT)wndParent.SendMessage( WM_NCHITTEST, 0, MAKELPARAM( ptMouse.x, ptMouse.y ) );
		
		// forward notifcation through to parent
		if ( nHitTest == HTCLIENT )
		{
			wndParent.ScreenToClient( &ptMouse );
			wndParent.PostMessage( nMessage, wParam, MAKELPARAM( ptMouse.x, ptMouse.y ) );
		}
		else
		{
			switch ( nMessage )
			{
				case WM_LBUTTONDOWN:	wndParent.PostMessage( WM_NCLBUTTONDOWN, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_LBUTTONUP:		wndParent.PostMessage( WM_NCLBUTTONUP, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_LBUTTONDBLCLK:	wndParent.PostMessage( WM_NCLBUTTONDBLCLK, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_RBUTTONDOWN:	wndParent.PostMessage( WM_NCRBUTTONDOWN, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_RBUTTONUP:		wndParent.PostMessage( WM_NCRBUTTONUP, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_RBUTTONDBLCLK:	wndParent.PostMessage( WM_NCRBUTTONDBLCLK, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_MBUTTONDOWN:	wndParent.PostMessage( WM_NCMBUTTONDOWN, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_MBUTTONUP:		wndParent.PostMessage( WM_NCMBUTTONUP, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
				case WM_MBUTTONDBLCLK:	wndParent.PostMessage( WM_NCMBUTTONDBLCLK, nHitTest, MAKELPARAM( ptMouse.x, ptMouse.y ) );
										break;
			}
		}

		return 0;
	}
	
	BOOL OnEraseBkgnd( HDC /*dc*/ ) 
	{
		return TRUE;
	}
	
	void OnPaint( HDC /*dc*/)
	{
		CPaintDC dcPaint( m_hWnd );
		
		int nContextState = dcPaint.SaveDC();
		
		CRect rcClient;
		GetClientRect( rcClient );
		
		CRect rcTitleTip( rcClient );
					
		dcPaint.SetBkColor( m_rgbBackground );
		dcPaint.ExtTextOut( rcTitleTip.left, rcTitleTip.top, ETO_OPAQUE, rcTitleTip, _T( "" ), 0, NULL );
		
		CBrush bshTitleFrame;
		bshTitleFrame.CreateSolidBrush( m_rgbTextColour );
		dcPaint.FrameRect( rcTitleTip, bshTitleFrame );
		
		int nTextLength = GetWindowTextLength();
		stdstr strItemText;
		strItemText.reserve(nTextLength + 1);
		strItemText.resize(nTextLength);
		GetWindowText( (LPTSTR)strItemText.c_str(), nTextLength + 1 );
		
		dcPaint.SelectFont( m_fntTitleFont );
		dcPaint.SetTextColor( m_rgbTextColour );
		dcPaint.SetBkMode( TRANSPARENT );
		
		CRect rcItemText( rcClient );
		rcItemText.OffsetRect( 4, 0 );
		
		dcPaint.DrawText( strItemText.c_str(), strItemText.length(), rcItemText, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER );
	
		dcPaint.RestoreDC( nContextState );
	}
};
