/*
UserinfoEx plugin for Miranda IM

Copyright:
� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol

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.
*/

#include "commonheaders.h"
#include "ctrl_combo.h"

/**
 * This static method creates an object for the CCombo class and returns its pointer.
 * If combobox is filled with items from pList.
 *
 * @param		hDlg			- HWND of the owning propertysheet page
 * @param		idCtrl			- the ID of the control to associate with this class's instance
 * @param		pszSetting		- the database setting to be handled by this class
 * @param		bDBDataType		- datatype of of the associated database setting (WORD, DWORD, ...)
 * @param		pList			- pointer to a LPIDSTRLIST list, which holds the items to insert into the combobox.
 * @param		nListCount		- number of items in the list
 *
 * @return	pointer of the newly created CCombo object.
 **/
CBaseCtrl* CCombo::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount)
{
	return new CCombo(hDlg, idCtrl, pszSetting, bDBDataType, pList, nListCount);
}

/**
 * This is the constructor. If combobox is filled with items from pList.
 *
 * @param		hDlg			- HWND of the owning propertysheet page
 * @param		idCtrl			- the ID of the control to associate with this class's instance
 * @param		pszSetting		- the database setting to be handled by this class
 * @param		bDBDataType		- datatype of of the associated database setting (WORD, DWORD, ...)
 * @param		pList			- pointer to a LPIDSTRLIST list, which holds the items to insert into the combobox.
 * @param		nListCount		- number of items in the list
 *
 * @return	nothing
 **/
CCombo::CCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount)
: CBaseCtrl(hDlg, idCtrl, pszSetting)
{
	_curSel = CB_ERR;
	_pList = pList;
	_nList = nListCount;
	_bDataType = bDBDataType;

	// fill in data
	if (_pList && (_nList > 0))
	{
		for (INT i = 0; i < _nList; i++)
		{
			AddItem(_pList[i].ptszTranslated, (LPARAM)&_pList[i]);
		}
	}
}

/**
 * This method searches for an item which matches the given nID.
 *
 * @param		nID				- the id of the desired object.
 *
 * @retval	CB_ERR	- item not found
 * @retval	0...n		- index of the combobox item
 **/
INT CCombo::Find(INT nID) const
{
	INT i;
	LPIDSTRLIST pd;

	for (i = ComboBox_GetCount(_hwnd) - 1; i >= 0; i--)
	{
		pd = (LPIDSTRLIST)ComboBox_GetItemData(_hwnd, i);
		if (PtrIsValid(pd) && (pd->nID == nID))
		{
			break;
		}
	}
	return i;
}

/**
 * This method searches for an item which matches the given label.
 *
 * @param		ptszItemLabel	- The translated label of the desired item.
 *
 * @retval	CB_ERR	- item not found
 * @retval	0...n		- index of the combobox item
 **/
INT CCombo::Find(LPTSTR ptszItemLabel) const
{
	return ComboBox_FindStringExact(_hwnd, 0, ptszItemLabel);
}

/**
 * Adds a string and associated item data to a combobox.
 *
 * @param	 pszText			- the text to add to the combobox
 * @param	 lParam				- item data to accociate with the new item
 *
 * @return	zero-based index to new item or CB_ERR on failure
 **/
INT_PTR CCombo::AddItem(LPCTSTR pszText, LPARAM lParam)
{
	INT_PTR added = ComboBox_AddString(_hwnd, pszText);
	if (SUCCEEDED(added)) 
	{
		if (PtrIsValid(lParam) && FAILED(ComboBox_SetItemData(_hwnd, added, lParam)))
		{
			ComboBox_DeleteString(_hwnd, added);
			added = CB_ERR;
		}
	}
	return added;
}

/**
 * This functions removes the user data from a combobox.
 *
 * @param		hCtrl			- HWND of the combobox
 *
 * @return	nothing
 **/
VOID CCombo::Release()
{
	delete this;
}

/**
 * This method selects the combobox item which matches the contact's setting, associated with.
 *
 * @param		hContact	- HANDLE of the contact
 * @param		pszProto	- the contact's protocol
 *
 * @return	nothing
 **/
BOOL CCombo::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
{
	if (!_Flags.B.hasChanged)
	{
		DBVARIANT dbv;
		LPIDSTRLIST pItem = NULL;
		INT iVal = CB_ERR;

		_Flags.B.hasCustom = _Flags.B.hasProto = _Flags.B.hasMeta = 0;
		_Flags.W |= DB::Setting::GetTStringCtrl(hContact, USERINFO, USERINFO, pszProto, _pszSetting, &dbv);
		EnableWindow(_hwnd, !hContact || _Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0));	

		if (_Flags.B.hasCustom || _Flags.B.hasProto || _Flags.B.hasMeta)
		{
			switch (dbv.type) 
			{
			case DBVT_BYTE:		iVal = Find((INT)dbv.bVal);		break;
			case DBVT_WORD:		iVal = Find((INT)dbv.wVal);		break;
			case DBVT_DWORD:	iVal = Find((INT)dbv.dVal);		break;
			case DBVT_TCHAR:
				iVal = Find(TranslateTS(dbv.ptszVal));
				if (iVal == CB_ERR) {
					// other
					iVal = Find(_pList[_nList - 1].nID);
				}
			}
		}
		if (iVal == CB_ERR)
		{
			// unspecified
			iVal = Find(_pList[0].nID);
		}
		DB::Variant::Free(&dbv);
		ComboBox_SetCurSel(_hwnd, iVal);
		_curSel = ComboBox_GetCurSel(_hwnd);
		SendMessage(GetParent(_hwnd), WM_COMMAND, MAKEWPARAM( (WORD)this->_idCtrl, (WORD)CBN_SELCHANGE), (LPARAM)_hwnd);
	}
	return _Flags.B.hasChanged;
}

/**
 * This method writes the combobox's item
 *
 * @param		hContact		- HANDLE of the contact
 * @param		pszProto		- the contact's protocol
 *
 * @return	nothing
 **/
VOID CCombo::OnApply(HANDLE hContact, LPCSTR pszProto)
{
	if (_Flags.B.hasChanged)
	{
		LPCSTR pszModule = hContact ? USERINFO : pszProto;

		if ((_Flags.B.hasCustom || !hContact) && (_curSel != CB_ERR))
		{
			LPIDSTRLIST pd;

			pd = (LPIDSTRLIST)SendMessage(_hwnd, CB_GETITEMDATA, _curSel, 0);
			if (pd != NULL)
			{
				switch (_bDataType)
				{
				case DBVT_BYTE:
					DB::Setting::WriteByte(hContact, pszModule, _pszSetting, pd->nID);
					break;
				case DBVT_WORD:
					DB::Setting::WriteWord(hContact, pszModule, _pszSetting, pd->nID);
					break;
				case DBVT_DWORD:
					DB::Setting::WriteDWord(hContact, pszModule, _pszSetting, pd->nID);
					break;
				case DBVT_ASCIIZ:
				case DBVT_WCHAR:
					DB::Setting::WriteAString(hContact, pszModule, _pszSetting, (LPSTR)pd->pszText);
				}
				if (!hContact)
				{
					_Flags.B.hasCustom = 0;
					_Flags.B.hasProto = 1;
				}
				_Flags.B.hasChanged = 0;
			}
		}
		if (_Flags.B.hasChanged)
		{
			DB::Setting::Delete(hContact, pszModule, _pszSetting);
			_Flags.B.hasChanged = 0;
			OnInfoChanged(hContact, pszProto);
		}
		InvalidateRect(_hwnd, NULL, TRUE);
	}
}

/**
 * The user changed combobox selection, so mark it changed.
 *
 * @return	nothing
 **/
VOID CCombo::OnChangedByUser(WORD wChangedMsg)
{
	if (wChangedMsg == CBN_SELCHANGE)
	{
		INT c = ComboBox_GetCurSel(_hwnd);

		if (_curSel != c)
		{
			if (!_Flags.B.hasChanged)
			{
				_Flags.B.hasChanged = 1;
				_Flags.B.hasCustom = 1;
				SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
			}
			_curSel = c;
		}
	}
}