/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
// Copyright (c) 2012-18 Miranda NG team,
// Copyright (c) 2000-09 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.
//
// part of tabSRMM messaging plugin for Miranda.
//
// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
//
// highlighter class for multi user chats

#include "stdafx.h"

void Chat_ApplyOptions();

void CMUCHighlight::cleanup()
{
	mir_free(m_NickPatternString);
	mir_free(m_TextPatternString);
	m_TextPatternString = m_NickPatternString = nullptr;

	mir_free(m_NickPatterns);
	mir_free(m_TextPatterns);
	m_iNickPatterns = m_iTextPatterns = 0;
	m_NickPatterns = m_TextPatterns = nullptr;
}

void CMUCHighlight::init()
{
	DBVARIANT dbv = { 0 };

	if (m_fInitialized)
		cleanup();							// clean up first, if we were already initialized

	m_fInitialized = true;

	if (0 == db_get_ws(0, CHAT_MODULE, "HighlightWords", &dbv)) {
		m_TextPatternString = dbv.ptszVal;
		_wsetlocale(LC_ALL, L"");
		wcslwr(m_TextPatternString);
	}

	if (0 == db_get_ws(0, CHAT_MODULE, "HighlightNames", &dbv))
		m_NickPatternString = dbv.ptszVal;

	m_dwFlags = M.GetByte(CHAT_MODULE, "HighlightEnabled", MATCH_TEXT);
	m_fHighlightMe = (M.GetByte(CHAT_MODULE, "HighlightMe", 1) ? true : false);

	tokenize(m_TextPatternString, m_TextPatterns, m_iTextPatterns);
	tokenize(m_NickPatternString, m_NickPatterns, m_iNickPatterns);
}

void CMUCHighlight::tokenize(wchar_t *tszString, wchar_t**& patterns, UINT& nr)
{
	if (tszString == nullptr)
		return;

	wchar_t	*p = tszString;

	if (*p == 0)
		return;

	nr = 0;

	if (*p != ' ')
		nr++;

	while (*p) {
		if (*p == ' ') {
			p++;
			while (*p && iswspace(*p))
				p++;
			if (*p)
				nr++;
		}
		p++;
	}
	patterns = (wchar_t **)mir_alloc(nr * sizeof(wchar_t*));

	p = tszString;
	nr = 0;

	if (*p != ' ')
		patterns[nr++] = p;

	while (*p) {
		if (*p == ' ') {
			*p++ = 0;
			while (*p && iswspace(*p))
				p++;
			if (*p)
				patterns[nr++] = p;
		}
		p++;
	}
}

bool CMUCHighlight::match(const GCEVENT *pgce, const SESSION_INFO *psi, DWORD dwFlags)
{
	int result = 0, nResult = 0;

	if (pgce == nullptr || m_Valid == false)
		return false;

	if ((m_dwFlags & MATCH_TEXT) && (dwFlags & MATCH_TEXT) && (m_fHighlightMe || m_iTextPatterns > 0) && psi != nullptr) {
		wchar_t	*p = pci->RemoveFormatting(pgce->ptszText);
		p = NEWWSTR_ALLOCA(p);
		if (p == nullptr)
			return false;
		CharLower(p);

		wchar_t	*tszMe = ((psi && psi->pMe) ? NEWWSTR_ALLOCA(psi->pMe->pszNick) : nullptr);
		if (tszMe)
			CharLower(tszMe);

		if (m_fHighlightMe && tszMe) {
			result = wcsstr(p, tszMe) ? MATCH_TEXT : 0;
			if (0 == m_iTextPatterns)
				goto skip_textpatterns;
		}

		while (p && !result) {
			while (*p && (*p == ' ' || *p == ',' || *p == '.' || *p == ':' || *p == ';' || *p == '?' || *p == '!'))
				p++;

			if (*p == 0)
				break;

			wchar_t *p1 = p;
			while (*p1 && *p1 != ' ' && *p1 != ',' && *p1 != '.' && *p1 != ':' && *p1 != ';' && *p1 != '?' && *p1 != '!')
				p1++;

			if (*p1)
				*p1 = 0;
			else
				p1 = nullptr;

			for (UINT i = 0; i < m_iTextPatterns && !result; i++)
				result = wildcmpw(p, m_TextPatterns[i]) ? MATCH_TEXT : 0;

			if (p1) {
				*p1 = ' ';
				p = p1 + 1;
			}
			else p = nullptr;
		}
	}

skip_textpatterns:

	// optionally, match the nickname against the list of nicks to highlight
	if ((m_dwFlags & MATCH_NICKNAME) && (dwFlags & MATCH_NICKNAME) && pgce->ptszNick && m_iNickPatterns > 0) {
		for (UINT i = 0; i < m_iNickPatterns && !nResult; i++) {
			if (pgce->ptszNick)
				nResult = wildcmpw(pgce->ptszNick, m_NickPatterns[i]) ? MATCH_NICKNAME : 0;
			if ((m_dwFlags & MATCH_UIN) && pgce->ptszUserInfo)
				nResult = wildcmpw(pgce->ptszUserInfo, m_NickPatterns[i]) ? MATCH_NICKNAME : 0;
		}
	}

	return result || nResult;
}

/**
 * Dialog procedure to handle global highlight settings
 *
 * @param Standard Windows dialog procedure parameters
 */
INT_PTR CALLBACK CMUCHighlight::dlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_INITDIALOG:
		TranslateDialogDefault(hwndDlg);
		{
			DBVARIANT dbv = { 0 };
			if (!db_get_ws(0, CHAT_MODULE, "HighlightWords", &dbv)) {
				::SetDlgItemText(hwndDlg, IDC_HIGHLIGHTTEXTPATTERN, dbv.ptszVal);
				::db_free(&dbv);
			}

			if (!db_get_ws(0, CHAT_MODULE, "HighlightNames", &dbv)) {
				::SetDlgItemText(hwndDlg, IDC_HIGHLIGHTNICKPATTERN, dbv.ptszVal);
				::db_free(&dbv);
			}

			DWORD dwFlags = M.GetByte(CHAT_MODULE, "HighlightEnabled", MATCH_TEXT);

			::CheckDlgButton(hwndDlg, IDC_HIGHLIGHTNICKENABLE, dwFlags & MATCH_NICKNAME ? BST_CHECKED : BST_UNCHECKED);
			::CheckDlgButton(hwndDlg, IDC_HIGHLIGHTNICKUID, dwFlags & MATCH_UIN ? BST_CHECKED : BST_UNCHECKED);
			::CheckDlgButton(hwndDlg, IDC_HIGHLIGHTTEXTENABLE, dwFlags & MATCH_TEXT ? BST_CHECKED : BST_UNCHECKED);
			::CheckDlgButton(hwndDlg, IDC_HIGHLIGHTME, M.GetByte(CHAT_MODULE, "HighlightMe", 1) ? BST_CHECKED : BST_UNCHECKED);

			::SendMessage(hwndDlg, WM_USER + 100, 0, 0);
		}
		return TRUE;

	case WM_USER + 100:
		Utils::enableDlgControl(hwndDlg, IDC_HIGHLIGHTTEXTPATTERN,
			::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTTEXTENABLE) ? TRUE : FALSE);

		Utils::enableDlgControl(hwndDlg, IDC_HIGHLIGHTNICKPATTERN,
			::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTNICKENABLE) ? TRUE : FALSE);

		Utils::enableDlgControl(hwndDlg, IDC_HIGHLIGHTNICKUID,
			::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTNICKENABLE) ? TRUE : FALSE);

		Utils::enableDlgControl(hwndDlg, IDC_HIGHLIGHTME,
			::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTTEXTENABLE) ? TRUE : FALSE);
		return FALSE;

	case WM_COMMAND:
		if ((LOWORD(wParam) == IDC_HIGHLIGHTNICKPATTERN
			|| LOWORD(wParam) == IDC_HIGHLIGHTTEXTPATTERN)
			&& (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != ::GetFocus()))
			return 0;

		::SendMessage(hwndDlg, WM_USER + 100, 0, 0);
		if (lParam != 0)
			::SendMessage(::GetParent(hwndDlg), PSM_CHANGED, 0, 0);
		break;

	case WM_NOTIFY:
		switch (((LPNMHDR)lParam)->idFrom) {
		case 0:
			switch (((LPNMHDR)lParam)->code) {
			case PSN_WIZFINISH:
				Chat_ApplyOptions();
				break;

			case PSN_APPLY:
				wchar_t*	szBuf = nullptr;
				int iLen = ::GetWindowTextLength(::GetDlgItem(hwndDlg, IDC_HIGHLIGHTNICKPATTERN));
				if (iLen) {
					szBuf = reinterpret_cast<wchar_t *>(mir_alloc((iLen + 2) * sizeof(wchar_t)));
					::GetDlgItemText(hwndDlg, IDC_HIGHLIGHTNICKPATTERN, szBuf, iLen + 1);
					db_set_ws(0, CHAT_MODULE, "HighlightNames", szBuf);
				}
				else db_set_ws(0, CHAT_MODULE, "HighlightNames", L"");

				iLen = ::GetWindowTextLength(::GetDlgItem(hwndDlg, IDC_HIGHLIGHTTEXTPATTERN));
				if (iLen) {
					szBuf = reinterpret_cast<wchar_t *>(mir_realloc(szBuf, sizeof(wchar_t) * (iLen + 2)));
					::GetDlgItemText(hwndDlg, IDC_HIGHLIGHTTEXTPATTERN, szBuf, iLen + 1);
					db_set_ws(0, CHAT_MODULE, "HighlightWords", szBuf);
				}
				else db_set_ws(0, CHAT_MODULE, "HighlightWords", L"");

				mir_free(szBuf);
				BYTE dwFlags = (::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTNICKENABLE) ? MATCH_NICKNAME : 0) |
					(::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTTEXTENABLE) ? MATCH_TEXT : 0);

				if (dwFlags & MATCH_NICKNAME)
					dwFlags |= (::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTNICKUID) ? MATCH_UIN : 0);

				db_set_b(0, CHAT_MODULE, "HighlightEnabled", dwFlags);
				db_set_b(0, CHAT_MODULE, "HighlightMe", ::IsDlgButtonChecked(hwndDlg, IDC_HIGHLIGHTME) ? 1 : 0);
				g_Settings.Highlight->init();
				return TRUE;
			}
		}
		break;
	}
	return FALSE;
}