/*
Scriver

Copyright (c) 2000-12 Miranda ICQ/IM project,

all portions of this codebase are copyrighted to the people
listed in contributors.txt.

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 "stdafx.h"

#ifndef TTI_NONE
#define TTI_NONE 0
#endif

const char *filename = "scriver.log";

void logInfo(const char *fmt, ...)
{
	SYSTEMTIME time;
	char *str;
	va_list vararg;
	int strsize;
	FILE *flog = fopen(filename, "at");
	if (flog != NULL) {
		GetLocalTime(&time);
		va_start(vararg, fmt);
		str = (char*)malloc(strsize = 2048);
		while (mir_vsnprintf(str, strsize, fmt, vararg) == -1)
			str = (char*)realloc(str, strsize += 2048);
		va_end(vararg);
		fprintf(flog, "%04d-%02d-%02d %02d:%02d:%02d,%03d [%s]", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds, "INFO");
		fprintf(flog, "  %s\n", str);
		free(str);
		fclose(flog);
	}
}

int GetRichTextLength(HWND hwnd, int codepage, BOOL inBytes)
{
	GETTEXTLENGTHEX gtl;
	gtl.codepage = codepage;
	if (inBytes) {
		gtl.flags = GTL_NUMBYTES;
	}
	else {
		gtl.flags = GTL_NUMCHARS;
	}
	gtl.flags |= GTL_PRECISE | GTL_USECRLF;
	return (int)SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
}

char* GetRichTextUtf(HWND hwnd)
{
	int textBufferSize = GetRichTextLength(hwnd, CP_UTF8, TRUE);
	if (textBufferSize == 0)
		return NULL;

	textBufferSize++;
	char *textBuffer = (char*)mir_alloc(textBufferSize);

	GETTEXTEX  gt = { 0 };
	gt.cb = textBufferSize;
	gt.flags = GT_USECRLF;
	gt.codepage = CP_UTF8;
	SendMessage(hwnd, EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)textBuffer);
	return textBuffer;
}

int SetRichText(HWND hwnd, const wchar_t *text)
{
	SETTEXTEX st;
	st.flags = ST_DEFAULT;
	st.codepage = 1200;
	SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text);

	return GetRichTextLength(hwnd, 1200, FALSE);
}

int SetRichTextRTF(HWND hwnd, const char *text)
{
	SETTEXTEX st;
	st.flags = ST_DEFAULT;
	st.codepage = CP_UTF8;
	SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text);

	return GetRichTextLength(hwnd, 1200, FALSE);
}

static DWORD CALLBACK RichTextStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
{
	static DWORD dwRead;
	char **ppText = (char**)dwCookie;

	if (*ppText == NULL) {
		*ppText = (char*)mir_alloc(cb + 1);
		memcpy(*ppText, pbBuff, cb);
		(*ppText)[cb] = 0;
		*pcb = cb;
		dwRead = cb;
	}
	else {
		char *p = (char*)mir_alloc(dwRead + cb + 1);
		memcpy(p, *ppText, dwRead);
		memcpy(p + dwRead, pbBuff, cb);
		p[dwRead + cb] = 0;
		mir_free(*ppText);
		*ppText = p;
		*pcb = cb;
		dwRead += cb;
	}

	return 0;
}

char* GetRichTextRTF(HWND hwnd)
{
	if (hwnd == 0)
		return NULL;

	char *pszText = NULL;
	EDITSTREAM stream = { 0 };
	stream.pfnCallback = RichTextStreamCallback;
	stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer
	SendMessage(hwnd, EM_STREAMOUT, SF_RTFNOOBJS | SFF_PLAINRTF | SF_USECODEPAGE | (CP_UTF8 << 16), (LPARAM)&stream);
	return pszText; // pszText contains the text
}

void rtrimText(wchar_t *text)
{
	static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n";
	size_t iLen = mir_wstrlen(text) - 1;
	while (wcschr(szTrimString, text[iLen])) {
		text[iLen] = '\0';
		iLen--;
	}
}

wchar_t* limitText(wchar_t *text, int limit)
{
	size_t len = mir_wstrlen(text);
	if (len > g_dat.limitNamesLength) {
		wchar_t *ptszTemp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (limit + 4));
		wcsncpy(ptszTemp, text, limit + 1);
		wcsncpy(ptszTemp + limit, L"...", 4);
		return ptszTemp;
	}
	return text;
}

wchar_t* GetRichTextWord(HWND hwnd, POINTL *ptl)
{
	long iCharIndex, start, end, iRes;
	wchar_t *pszWord = GetRichEditSelection(hwnd);
	if (pszWord == NULL) {
		iCharIndex = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)ptl);
		if (iCharIndex >= 0) {
			start = SendMessage(hwnd, EM_FINDWORDBREAK, WB_LEFT, iCharIndex); //-iChars;
			end = SendMessage(hwnd, EM_FINDWORDBREAK, WB_RIGHT, iCharIndex); //-iChars;
			if (end - start > 0) {
				TEXTRANGE tr;
				CHARRANGE cr;
				memset(&tr, 0, sizeof(TEXTRANGE));
				pszWord = (wchar_t*)mir_alloc(sizeof(wchar_t) * (end - start + 1));
				cr.cpMin = start;
				cr.cpMax = end;
				tr.chrg = cr;
				tr.lpstrText = pszWord;
				iRes = SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
				if (iRes <= 0) {
					mir_free(pszWord);
					pszWord = NULL;
				}
			}
		}
	}
	if (pszWord != NULL)
		rtrimText(pszWord);

	return pszWord;
}

static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
{
	MessageSendQueueItem *msi = (MessageSendQueueItem *)dwCookie;
	msi->sendBuffer = (char*)mir_realloc(msi->sendBuffer, msi->sendBufferSize + cb + 2);
	memcpy(msi->sendBuffer + msi->sendBufferSize, pbBuff, cb);
	msi->sendBufferSize += cb;
	*((wchar_t*)(msi->sendBuffer + msi->sendBufferSize)) = '\0';
	*pcb = cb;
	return 0;
}

wchar_t *GetRichEditSelection(HWND hwnd)
{
	CHARRANGE sel;
	SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
	if (sel.cpMin == sel.cpMax)
		return NULL;
		
	MessageSendQueueItem msi;
	msi.sendBuffer = NULL;
	msi.sendBufferSize = 0;

	EDITSTREAM stream;
	memset(&stream, 0, sizeof(stream));
	stream.pfnCallback = StreamOutCallback;
	stream.dwCookie = (DWORD_PTR)&msi;
	SendMessage(hwnd, EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream);
	return (wchar_t*)msi.sendBuffer;
}

void AppendToBuffer(char *&buffer, size_t &cbBufferEnd, size_t &cbBufferAlloced, const char *fmt, ...)
{
	va_list va;
	int charsDone;

	va_start(va, fmt);
	for (;;) {
		charsDone = mir_vsnprintf(buffer + cbBufferEnd, cbBufferAlloced - cbBufferEnd, fmt, va);
		if (charsDone >= 0)
			break;
		cbBufferAlloced += 1024;
		buffer = (char*)mir_realloc(buffer, cbBufferAlloced);
	}
	va_end(va);
	cbBufferEnd += charsDone;
}


int MeasureMenuItem(WPARAM, LPARAM lParam)
{
	LPMEASUREITEMSTRUCT mis = (LPMEASUREITEMSTRUCT)lParam;
	if (mis->itemData != (ULONG_PTR)g_dat.hButtonIconList && mis->itemData != (ULONG_PTR)g_dat.hSearchEngineIconList && mis->itemData != (ULONG_PTR)g_dat.hChatButtonIconList)
		return FALSE;

	mis->itemWidth = max(0, GetSystemMetrics(SM_CXSMICON) - GetSystemMetrics(SM_CXMENUCHECK) + 4);
	mis->itemHeight = GetSystemMetrics(SM_CYSMICON) + 2;
	return TRUE;
}

int DrawMenuItem(WPARAM, LPARAM lParam)
{
	int y;
	int id;
	LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
	if (dis->itemData != (ULONG_PTR)g_dat.hButtonIconList && dis->itemData != (ULONG_PTR)g_dat.hSearchEngineIconList && dis->itemData != (ULONG_PTR)g_dat.hChatButtonIconList) {
		return FALSE;
	}
	id = dis->itemID;
	if (id >= IDM_SEARCH_GOOGLE) {
		id -= IDM_SEARCH_GOOGLE;
	}
	y = (dis->rcItem.bottom - dis->rcItem.top - GetSystemMetrics(SM_CYSMICON)) / 2 + 1;
	if (dis->itemState & ODS_SELECTED) {
		if (dis->itemState & ODS_CHECKED) {
			RECT rc;
			rc.left = 2;
			rc.right = GetSystemMetrics(SM_CXSMICON) + 2;
			rc.top = y;
			rc.bottom = rc.top + GetSystemMetrics(SM_CYSMICON) + 2;
			FillRect(dis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
			ImageList_DrawEx((HIMAGELIST)dis->itemData, id, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_DEFAULT, ILD_SELECTED);
		}
		else
			ImageList_DrawEx((HIMAGELIST)dis->itemData, id, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_DEFAULT, ILD_FOCUS);
	}
	else {
		if (dis->itemState & ODS_CHECKED) {
			HBRUSH hBrush;
			RECT rc;
			COLORREF menuCol, hiliteCol;
			rc.left = 0;
			rc.right = GetSystemMetrics(SM_CXSMICON) + 4;
			rc.top = y - 2;
			rc.bottom = rc.top + GetSystemMetrics(SM_CYSMICON) + 4;
			DrawEdge(dis->hDC, &rc, BDR_SUNKENOUTER, BF_RECT);
			InflateRect(&rc, -1, -1);
			menuCol = GetSysColor(COLOR_MENU);
			hiliteCol = GetSysColor(COLOR_3DHIGHLIGHT);
			hBrush = CreateSolidBrush(RGB
				((GetRValue(menuCol) + GetRValue(hiliteCol)) / 2, (GetGValue(menuCol) + GetGValue(hiliteCol)) / 2,
					(GetBValue(menuCol) + GetBValue(hiliteCol)) / 2));
			FillRect(dis->hDC, &rc, hBrush);
			DeleteObject(hBrush);
			ImageList_DrawEx((HIMAGELIST)dis->itemData, id, dis->hDC, 2, y, 0, 0, CLR_NONE, GetSysColor(COLOR_MENU), ILD_BLEND25);
		}
		else
			ImageList_DrawEx((HIMAGELIST)dis->itemData, id, dis->hDC, 2, y, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL);
	}
	return TRUE;
}

// Code taken from http://www.geekhideout.com/urlcode.shtml

/* Converts an integer value to its hex character*/
char to_hex(char code)
{
	static char hex[] = "0123456789abcdef";
	return hex[code & 15];
}

/* Returns a url-encoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_encode(char *str)
{
	char *pstr = str, *buf = (char*)mir_alloc(mir_strlen(str) * 3 + 1), *pbuf = buf;
	while (*pstr) {
		if ((48 <= *pstr && *pstr <= 57) ||//0-9
			(65 <= *pstr && *pstr <= 90) ||//ABC...XYZ
			(97 <= *pstr && *pstr <= 122) ||//abc...xyz
			*pstr == '-' || *pstr == '_' || *pstr == '.')
			*pbuf++ = *pstr;
		else if (*pstr == ' ')
			*pbuf++ = '+';
		else
			*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
		pstr++;
	}
	*pbuf = '\0';
	return buf;
}

void SearchWord(wchar_t *word, int engine)
{
	char szURL[4096];
	if (word && word[0]) {
		T2Utf wordUTF(word);
		ptrA wordURL(mir_urlEncode(wordUTF));
		switch (engine) {
		case SEARCHENGINE_WIKIPEDIA:
			mir_snprintf(szURL, "http://en.wikipedia.org/wiki/%s", wordURL);
			break;
		case SEARCHENGINE_YAHOO:
			mir_snprintf(szURL, "http://search.yahoo.com/search?p=%s&ei=UTF-8", wordURL);
			break;
		case SEARCHENGINE_FOODNETWORK:
			mir_snprintf(szURL, "http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", wordURL);
			break;
		case SEARCHENGINE_BING:
			mir_snprintf(szURL, "http://www.bing.com/search?q=%s&form=OSDSRC", wordURL);
			break;
		case SEARCHENGINE_GOOGLE_MAPS:
			mir_snprintf(szURL, "http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", wordURL);
			break;
		case SEARCHENGINE_GOOGLE_TRANSLATE:
			mir_snprintf(szURL, "http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", wordURL);
			break;
		case SEARCHENGINE_YANDEX:
			mir_snprintf(szURL, "http://yandex.ru/yandsearch?text=%s", wordURL);
			break;
		case SEARCHENGINE_GOOGLE:
		default:
			mir_snprintf(szURL, "http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", wordURL);
			break;
		}

		Utils_OpenUrl(szURL);
	}
}

void SetSearchEngineIcons(HMENU hMenu, HIMAGELIST hImageList)
{
	for (int i = 0; i < IDI_LASTICON - IDI_GOOGLE; i++) {
		MENUITEMINFO mii = { 0 };
		mii.cbSize = sizeof(mii);
		mii.fMask = MIIM_BITMAP | MIIM_DATA;
		mii.hbmpItem = HBMMENU_CALLBACK;
		mii.dwItemData = (ULONG_PTR)hImageList;
		SetMenuItemInfo(hMenu, IDM_SEARCH_GOOGLE + i, FALSE, &mii);
	}
}

void GetContactUniqueId(SrmmWindowData *dat, char *buf, int maxlen)
{
	ptrW id(Contact_GetInfo(CNF_UNIQUEID, dat->hContact, dat->szProto));
	if (id != NULL)
		strncpy_s(buf, maxlen, _T2A(id), _TRUNCATE);
}

HWND CreateToolTip(HWND hwndParent, LPTSTR ptszText, LPTSTR ptszTitle, RECT *rect)
{
	TOOLINFO ti = { 0 };
	HWND hwndTT;
	hwndTT = CreateWindowEx(WS_EX_TOPMOST,
		TOOLTIPS_CLASS, NULL,
		WS_POPUP | TTS_NOPREFIX,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		hwndParent, NULL, g_hInst, NULL);

	SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	ti.cbSize = sizeof(TOOLINFO);
	ti.uFlags = TTF_SUBCLASS | TTF_CENTERTIP;
	ti.hwnd = hwndParent;
	ti.hinst = g_hInst;
	ti.lpszText = ptszText;
	ti.rect = *rect;
	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti);
	SendMessage(hwndTT, TTM_SETTITLE, TTI_NONE, (LPARAM)ptszTitle);
	return hwndTT;
}

void SetToolTipText(HWND hwndParent, HWND hwndTT, LPTSTR ptszText, LPTSTR ptszTitle)
{
	TOOLINFO ti = { sizeof(ti) };
	ti.hinst = g_hInst;
	ti.hwnd = hwndParent;
	ti.lpszText = ptszText;
	SendMessage(hwndTT, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
	SendMessage(hwndTT, TTM_SETTITLE, TTI_NONE, (LPARAM)ptszTitle);
}

void SetToolTipRect(HWND hwndParent, HWND hwndTT, RECT *rect)
{
	TOOLINFO ti = { sizeof(ti) };
	ti.hinst = g_hInst;
	ti.hwnd = hwndParent;
	ti.rect = *rect;
	SendMessage(hwndTT, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
}

void SetButtonsPos(HWND hwndDlg, bool bShow)
{
	HDWP hdwp = BeginDeferWindowPos(Srmm_GetButtonCount());

	RECT rc;
	GetWindowRect(GetDlgItem(hwndDlg, IDC_SPLITTERY), &rc);
	POINT pt = { 0, rc.top };
	ScreenToClient(hwndDlg, &pt);
	pt.y -= 20;

	int iLeftX = 2, iRightX = rc.right - rc.left - 2;
	int iGap = db_get_b(NULL, SRMMMOD, "ButtonsBarGap", 1);

	CustomButtonData *cbd;
	for (int i = 0; cbd = Srmm_GetNthButton(i); i++) {
		HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
		if (hwndButton == NULL)
			continue;

		ShowWindow(hwndButton, bShow ? SW_SHOW : SW_HIDE);

		int width = iGap + cbd->m_iButtonWidth;
		if (cbd->m_bRSided) {
			iRightX -= width;
			hdwp = DeferWindowPos(hdwp, hwndButton, NULL, iRightX, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
		}
		else {
			hdwp = DeferWindowPos(hdwp, hwndButton, NULL, iLeftX, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
			iLeftX += width;
		}
	}

	EndDeferWindowPos(hdwp);
}