#include "stdafx.h"
#include "CListControl.h"
#include "PaintUtils.h"

LRESULT CListControlTruncationTooltipImpl::OnTTShow(int,LPNMHDR,BOOL&) {
	SetTimer(KTooltipTimer,KTooltipTimerDelay);
	return 0;
}
LRESULT CListControlTruncationTooltipImpl::OnTTPop(int,LPNMHDR,BOOL&) {
	KillTimer(KTooltipTimer);
	return 0;
}
LRESULT CListControlTruncationTooltipImpl::OnTTGetDispInfo(int,LPNMHDR p_hdr,BOOL&) {
	LPNMTTDISPINFO info = (LPNMTTDISPINFO)p_hdr;

	info->lpszText = const_cast<TCHAR*>(this->m_tooltipText.get_ptr());
	info->hinst = 0;
	info->uFlags = 0;
		
	return 0;
}

LRESULT CListControlTruncationTooltipImpl::OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
	if (m_tooltip.m_hWnd != NULL) m_tooltip.DestroyWindow();
	KillTimer(KTooltipTimer);
	bHandled = FALSE; return 0;
}

CListControlTruncationTooltipImpl::CListControlTruncationTooltipImpl() 
	: m_toolinfo()
	, m_tooltipRect(0,0,0,0)
{
}



void CListControlTruncationTooltipImpl::TooltipRemove() {
	m_tooltipRect = CRect(0,0,0,0);
	if (m_tooltip.m_hWnd != NULL) {
		m_tooltip.TrackActivate(&m_toolinfo,FALSE);
	}
}

void CListControlTruncationTooltipImpl::TooltipRemoveCheck() {
	CPoint pt; ;
	if (GetCursorPos(&pt) && ScreenToClient(&pt)) {
		TooltipRemoveCheck( MAKELPARAM( pt.x, pt.y ) );
	}
}
void CListControlTruncationTooltipImpl::TooltipRemoveCheck(LPARAM pos) {
	if (!m_tooltipRect.IsRectEmpty()) {
		CPoint pt(pos);
		if (!GetClientRectHook().PtInRect(pt)) {
			TooltipRemove();
		} else {
			ClientToScreen(&pt);
			if (!m_tooltipRect.PtInRect(pt)) {
				TooltipRemove();
			}
		}
	}
}

LRESULT CListControlTruncationTooltipImpl::OnTimer(UINT,WPARAM wp,LPARAM,BOOL& bHandled) {
	switch(wp) {
		case KTooltipTimer:
			TooltipRemoveCheck();
			return 0;
		default:
			bHandled = FALSE;
			return 0;
	}
}

LRESULT CListControlTruncationTooltipImpl::OnMouseMovePassThru(UINT,WPARAM,LPARAM lp,BOOL& bHandled) {
	TooltipRemoveCheck(lp);
	{
		TRACKMOUSEEVENT ev = {sizeof(ev)};
		ev.dwFlags = TME_HOVER;
		ev.hwndTrack = *this;
		ev.dwHoverTime = HOVER_DEFAULT;
		TrackMouseEvent(&ev);
	}
	bHandled = FALSE;
	return 0;
}


bool CListControlTruncationTooltipImpl::IsRectPartiallyObscuredAbs(CRect const & r) const {
	const CRect cl = GetVisibleRectAbs();
	return r.right > cl.right || r.top < cl.top || r.bottom > cl.bottom;
}

bool CListControlTruncationTooltipImpl::IsRectFullyVisibleAbs(CRect const & r) {
	const CRect cl = GetVisibleRectAbs();
	return r.left >= cl.left && r.right <= cl.right && r.top >= cl.top && r.bottom <= cl.bottom;
}

bool CListControlTruncationTooltipImpl::GetTooltipData(CPoint pt, pfc::string_base & outText, CRect & outRC, CFontHandle & outFont) const {
	t_size item;
	if (ItemFromPointAbs(pt, item)) {
		PFC_ASSERT(item < GetItemCount());
		const CRect itemRectAbs = this->GetItemRectAbs(item);
		/*if (this->IsHeaderEnabled()) */{
			t_uint32 cbase = 0;
			auto orderArray = this->GetColumnOrderArray();
			for (t_size _cwalk = 0; _cwalk < orderArray.size(); ++_cwalk) {
				const t_size cwalk = orderArray[_cwalk];
				//const TColumnRuntime & col = m_columns[cwalk];

				const t_uint32 width = GetSubItemWidth(cwalk);
				if ((t_uint32)pt.x < cbase + width) {
					t_uint32 estWidth = GetOptimalSubItemWidthSimple(item, cwalk);
					CRect rc = itemRectAbs; rc.left = cbase; rc.right = cbase + estWidth;
					if (estWidth > width || (IsRectPartiallyObscuredAbs(rc) && rc.PtInRect(pt))) {
						pfc::string_formatter label, ccTemp;
						if (GetSubItemText(item, cwalk, label)) {
							PaintUtils::TextOutColors_StripCodes(ccTemp, label);
							outFont = GetFont(); outRC = rc; outText = ccTemp;
							return true;
						}
					}
					break;
				}
				cbase += width;
			}
		}
	} else if (GroupHeaderFromPointAbs2(pt, item)) {
		CRect rc;
		if (GetGroupHeaderRectAbs2(item, rc) && rc.PtInRect(pt)) {
			const t_uint32 estWidth = GetOptimalGroupHeaderWidth2( item );
			CRect rcText = rc; rcText.right = rcText.left + estWidth;
			if (estWidth > (t_uint32)rc.Width() || (IsRectPartiallyObscuredAbs(rcText) && rcText.PtInRect(pt))) {
				pfc::string_formatter label;
				if (GetGroupHeaderText2(item, label)) {
					outFont = GetGroupHeaderFont(); outRC = rc; outText = label; 
					return true;
				}
			}
		}
	}
	return false;
}
LRESULT CListControlTruncationTooltipImpl::OnHover(UINT,WPARAM wp,LPARAM lp,BOOL&) {
	if (!m_tooltipRect.IsRectEmpty()) {
		return 0;
	}
	if (wp & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) return 0;
	
	const CPoint pt = PointClientToAbs(CPoint(lp));

	CFontHandle font;
	CRect rc;
	pfc::string8 text;
	if ( this->GetTooltipData(pt, text, rc, font) ) {
		this->m_tooltipFont = font;
		// Gets stuck if the text is very long!
		if (text.length() < 4096) {
			TooltipActivateAbs(text, rc);
		}
	}
	return 0;
}

void CListControlTruncationTooltipImpl::TooltipActivateAbs(const char * label, const CRect & rect)  {
	CRect temp = RectAbsToClient(rect);
	ClientToScreen(temp);
	TooltipActivate(label,temp);
}
void CListControlTruncationTooltipImpl::TooltipActivate(const char * label, const CRect & rect) {
	if (rect.IsRectEmpty()) return;
	if (m_tooltip.m_hWnd == NULL) {
		try {
			InitTooltip();
		} catch(std::exception const & e) {
			(void) e;
			// console::complain("Tooltip initialization failure", e);
			return;
		}
	}
	
	m_tooltipText.convert( EscapeTooltipText( label ) );

	m_tooltipRect = rect;

	TooltipUpdateFont();
	m_tooltip.TrackPosition(rect.left,rect.top);
	m_tooltip.TrackActivate(&m_toolinfo,TRUE);
}

void CListControlTruncationTooltipImpl::TooltipUpdateFont() {
	if (m_tooltip.m_hWnd != NULL) {
		if (m_tooltipFont) {
			m_tooltip.SetFont(m_tooltipFont);
		}
	}
}

void CListControlTruncationTooltipImpl::InitTooltip() {
	m_tooltipRect = CRect(0,0,0,0);
	WIN32_OP( m_tooltip.Create(NULL,NULL,NULL,WS_POPUP,WS_EX_TRANSPARENT) );
	m_toolinfo.cbSize = sizeof(m_toolinfo);
	m_toolinfo.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_ABSOLUTE | TTF_TRANSPARENT;
	m_toolinfo.hwnd = *this;
	m_toolinfo.uId = 0;
	m_toolinfo.lpszText = LPSTR_TEXTCALLBACK;
	m_toolinfo.hinst = GetThisModuleHandle();
	WIN32_OP_D( m_tooltip.AddTool(&m_toolinfo) );
}
