#include <common/odlist.hpp>
#include <common/purehdc.hpp>
#include <common/rect.hpp>
#include <common/gdipoint.hpp>
#include <common/drawitem.hpp>
#include <common/measure.hpp>
#include <common/puredwrd.hpp>

OwnerDrawList::OwnerDrawList(GUIWindow &parentWnd,HWND hControlWnd,UINT controlID) 
: mParentWnd(parentWnd), Control(hControlWnd,controlID),
  mDlgWidthUnits((LOWORD(::GetDialogBaseUnits())/4)*2)
{
	mDrawItemHandler.setCallback(this,&OwnerDrawList::drawItemHandler);
	mMeasureItemHandler.setCallback(this,&OwnerDrawList::measureItemHandler);
	mControlColorHandler.setCallback(this,&OwnerDrawList::controlColorHandler);
	mSizeHandler.setCallback(this,&OwnerDrawList::sizeHandler);
	mParentWnd.insertHandler(VectorHandler::DrawItemHandler,&mDrawItemHandler);
	mParentWnd.insertHandler(VectorHandler::MeasureItemHandler,&mMeasureItemHandler);
	mParentWnd.insertHandler(VectorHandler::ControlColorHandler,&mControlColorHandler);
	mParentWnd.insertHandler(VectorHandler::SizeHandler,&mSizeHandler);
}

OwnerDrawList::OwnerDrawList(GUIWindow &parentWnd,const Rect &initRect,UINT controlID,DWORD style)
: mParentWnd(parentWnd), mDlgWidthUnits((LOWORD(::GetDialogBaseUnits())/4)*2)
{
	mDrawItemHandler.setCallback(this,&OwnerDrawList::drawItemHandler);
	mMeasureItemHandler.setCallback(this,&OwnerDrawList::measureItemHandler);
	mControlColorHandler.setCallback(this,&OwnerDrawList::controlColorHandler);
  mSizeHandler.setCallback(this,&OwnerDrawList::sizeHandler);
	mParentWnd.insertHandler(VectorHandler::DrawItemHandler,&mDrawItemHandler);
	mParentWnd.insertHandler(VectorHandler::MeasureItemHandler,&mMeasureItemHandler);
	mParentWnd.insertHandler(VectorHandler::ControlColorHandler,&mControlColorHandler);
	mParentWnd.insertHandler(VectorHandler::SizeHandler,&mSizeHandler);
  createControl("LISTBOX","",style,initRect,parentWnd,controlID);
}

OwnerDrawList::~OwnerDrawList()
{
	mParentWnd.removeHandler(VectorHandler::DrawItemHandler,&mDrawItemHandler);
	mParentWnd.removeHandler(VectorHandler::MeasureItemHandler,&mMeasureItemHandler);
	mParentWnd.removeHandler(VectorHandler::ControlColorHandler,&mControlColorHandler);
	mParentWnd.removeHandler(VectorHandler::SizeHandler,&mSizeHandler);
}

CallbackData::ReturnType OwnerDrawList::controlColorHandler(CallbackData &someCallbackData)
{
  PureDevice pureDevice((HDC)someCallbackData.wParam());
	Control wndListBox((HWND)someCallbackData.lParam(),::GetDlgCtrlID((HWND)someCallbackData.lParam()),FALSE);
	if(!(*this==wndListBox))return (CallbackData::ReturnType)TRUE;
	return handleControlColor(pureDevice,wndListBox);
}

CallbackData::ReturnType OwnerDrawList::drawItemHandler(CallbackData &someCallbackData)
{
  DrawItem drawItem(*((LPDRAWITEMSTRUCT)someCallbackData.lParam()));
	if(drawItem.controlID()!=controlID())return (CallbackData::ReturnType)TRUE;
	handleDraw(drawItem);
	return (CallbackData::ReturnType)TRUE;
}

CallbackData::ReturnType OwnerDrawList::measureItemHandler(CallbackData &someCallbackData)
{
  if(controlID()!=someCallbackData.wParam())return (CallbackData::ReturnType)TRUE;
  LPMEASUREITEMSTRUCT pMeasureItemStruct=(LPMEASUREITEMSTRUCT)someCallbackData.lParam();
  MeasureItem measureItem(*pMeasureItemStruct);
	handleMeasureItem(measureItem);
	pMeasureItemStruct->CtlType=measureItem.ctlType();
	pMeasureItemStruct->CtlID=measureItem.ctlID();
	pMeasureItemStruct->itemID=measureItem.itemID();
	pMeasureItemStruct->itemWidth=measureItem.itemWidth();
	pMeasureItemStruct->itemHeight=measureItem.itemHeight();
	pMeasureItemStruct->itemData=measureItem.itemData();
	return (CallbackData::ReturnType)TRUE;
}

CallbackData::ReturnType OwnerDrawList::sizeHandler(CallbackData &someCallbackData)
{
  handleResize(someCallbackData.loWord(),someCallbackData.hiWord());
  return (CallbackData::ReturnType)FALSE;
}

BOOL OwnerDrawList::setTabStops(Block<PureDWORD> &tabStops)
{
  if(!isOkay())return FALSE;
	mTabStopArray.size(tabStops.size());
	if(!mTabStopArray.size())return FALSE;
	int *pIntArray=(int*)&mTabStopArray[0];
	for(int itemIndex=0;itemIndex<tabStops.size();itemIndex++)pIntArray[itemIndex]=tabStops[itemIndex].getValue()*dlgWidthUnits();
  return sendMessage(LB_SETTABSTOPS,mTabStopArray.size(),(LPARAM)pIntArray);
}

LONG OwnerDrawList::addString(const String &stringData)
{
	int itemIndex;
	if(!isOkay()||stringData.isNull())return FALSE;
	sendMessage(LB_ADDSTRING,0,(LPARAM)(LPSTR)stringData);
	return TRUE;
}

LONG OwnerDrawList::insertString(const String &stringData,int insertPos)
{
  if(!isOkay()||stringData.isNull())return LB_ERR;
	return sendMessage(LB_INSERTSTRING,(WPARAM)insertPos,(LPARAM)(LPSTR)stringData);
}

LONG OwnerDrawList::insertStringAndMakeCurrent(const String &stringData)
{
  if(!isOkay()||stringData.isNull())return LB_ERR;
  if(LB_ERR==insertString(stringData))return LB_ERR;
	return setCurrent(getCount()-1);
}

LONG OwnerDrawList::deleteString(DWORD itemIndex)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_DELETESTRING,(WPARAM)itemIndex,0L);
}

LONG OwnerDrawList::getText(String &stringData,DWORD itemIndex)
{
  if(!isOkay())return LB_ERR;
  stringData.reserve(String::MaxString);
	return sendMessage(LB_GETTEXT,(WPARAM)itemIndex,(LPARAM)(LPSTR)stringData);
}

LONG OwnerDrawList::getSel(UINT itemIndex)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_GETSEL,(WPARAM)itemIndex,(LPARAM)0);
}

LONG OwnerDrawList::getCursorItem(void)
{
  GDIPoint cursorPoint;

	::GetCursorPos(&((POINT&)cursorPoint));
	::ScreenToClient(*this,&((POINT&)cursorPoint));
	return itemFromPoint(cursorPoint);
}

LONG OwnerDrawList::getItemRect(UINT itemIndex,Rect &someRect)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_GETITEMRECT,(WPARAM)itemIndex,(LPARAM)&((RECT&)someRect));
}

LONG OwnerDrawList::itemFromPoint(const GDIPoint &someGDIPoint)
{
  LONG itemCount;
	Rect itemRect;

  if(!isOkay())return LB_ERR;
	itemCount=getCount();
	for(int itemIndex=0;itemIndex<itemCount;itemIndex++)
	{
	  if(LB_ERR==getItemRect(itemIndex,itemRect))continue;
		if(::PtInRect(&((RECT&)itemRect),(POINT&)((GDIPoint&)someGDIPoint)))return itemIndex;
	}
	return LB_ERR;
}

LONG OwnerDrawList::setCaretIndex(int itemIndex,BOOL scrollIntoView)const
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_SETCARETINDEX,itemIndex,MAKELPARAM(scrollIntoView,0));
}

LONG OwnerDrawList::getSelItems(GlobalData<DWORD> &selectedItems)
{
  GlobalData<DWORD> selItems;
  LONG itemCount;

  if(!isOkay())return LB_ERR;
	selItems.size(MaxItems);
	itemCount=sendMessage(LB_GETSELITEMS,selItems.size(),(LPARAM)((DWORD*)&selItems[0]));
	if(LB_ERR==itemCount)return LB_ERR;
	selectedItems.size(itemCount);
	for(DWORD itemIndex=0;itemIndex<itemCount;itemIndex++)selectedItems[itemIndex]=selItems[itemIndex];
	return selectedItems.size();
}

LONG OwnerDrawList::setSelection(UINT itemIndex,WORD selectionState)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_SETSEL,selectionState,itemIndex);
}

LONG OwnerDrawList::getCount(void)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_GETCOUNT,0,0L);
}

LONG OwnerDrawList::getSelCount(void)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_GETSELCOUNT,0,0L);
}

WORD OwnerDrawList::setCurrent(WORD itemIndex)
{
	if(!isOkay())return FALSE;
	return sendMessage(LB_SETCURSEL,(WPARAM)itemIndex,0L);
}

LONG OwnerDrawList::getCurrent(void)
{
  if(!isOkay())return LB_ERR;
	return sendMessage(LB_GETCURSEL,0,0L);
}

LONG OwnerDrawList::findStringExact(const String &itemString,int startIndex)const
{
  if(itemString.isNull()||!isOkay())return LB_ERR;
  return sendMessage(LB_FINDSTRINGEXACT,startIndex,(LPARAM)(LPCTSTR)(LPSTR)(String&)itemString);
}

LONG OwnerDrawList::findString(const String &itemString,int startIndex)const
{
  if(itemString.isNull()||!isOkay())return LB_ERR;
	return sendMessage(LB_FINDSTRING,startIndex,(LPARAM)(LPCTSTR)(LPSTR)(String&)itemString);
}

LONG OwnerDrawList::selItemRange(UINT rangeFirst,UINT rangeLast,BOOL select)const
{
  if(!isOkay())return LB_ERR;
  return sendMessage(LB_SELITEMRANGE,(WPARAM)select,(LPARAM)MAKELPARAM(rangeFirst,rangeLast));  
}

LONG OwnerDrawList::textOut(const DrawItem &drawItem,const String &strData,int xLocation)
{
  TEXTMETRIC textMetric;
	LONG retCode(FALSE);
	Rect drawRect;

  if(!isOkay())return retCode;
	if(strData.isNull())return retCode;
	drawRect=drawItem.rectItem();
	::GetTextMetrics(drawItem.deviceContext(),&textMetric);
	if(mTabStopArray.size())retCode=::TabbedTextOut(drawItem.deviceContext(),xLocation*dlgWidthUnits(),(drawRect.bottom()+drawRect.top()-textMetric.tmHeight)/2,(LPSTR)(String&)strData,strData.length(),mTabStopArray.size(),(int*)&mTabStopArray[0],0);
	else retCode=::TextOut(drawItem.deviceContext(),xLocation*dlgWidthUnits(),(drawRect.bottom()+drawRect.top()-textMetric.tmHeight)/2,(LPSTR)(String&)strData,strData.length());
	return retCode;
}

// ******* virtuals

GUIWindow &OwnerDrawList::controlWindow(void)
{
  return *this;
}

LPARAM OwnerDrawList::handleControlColor(PureDevice &/*pureDevice*/,Control &/*wndListBox*/)
{
  return FALSE;
}

WORD OwnerDrawList::handleDraw(const DrawItem &drawItem)
{
  drawEntire(drawItem);
  return TRUE;
}

WORD OwnerDrawList::handleMeasureItem(MeasureItem &measureItem)
{
  measureItem.itemHeight(17);
  return TRUE;
}

void OwnerDrawList::drawEntire(const DrawItem &drawItem)
{
	String stringData;
	HGDIOBJ hPrevGDIObj;
	int strLen;

  if((((UINT)drawItem.itemState())&ODS_SELECTED))::SetBkColor(drawItem.deviceContext(),RGBColor(192,192,192));
	else ::SetBkColor(drawItem.deviceContext(),RGBColor(255,255,255));
	strLen=sendMessage(drawItem.hwndItem(),LB_GETTEXTLEN,drawItem.itemID(),(LPARAM)(LPSTR)stringData);
	stringData.reserve(strLen+1);
	sendMessage(drawItem.hwndItem(),LB_GETTEXT,drawItem.itemID(),(LPARAM)(LPSTR)stringData);
	stringData.length(180);
	textOut(drawItem,stringData,0);
}

void OwnerDrawList::handleResize(WORD /*width*/,WORD /*height*/)
{
}
