/*
 *  Smart Auto Replier (SAR) - auto replier plugin for Miranda IM
 *
 *  Copyright (C) 2004 - 2012 by Volodymyr M. Shcherbyna <volodymyr@shcherbyna.com>
 *
 *      This file is part of SAR.
 *
 *  SAR 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  SAR 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 SAR.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "stdafx.h"
#include "resource.h"

/// these dlg are necessary here..
#include "gui\settingsdlgholder.h"
#include "gui\EditReplyDlg.h" 
#include "gui\addruledlg.h"

#include "../updater/m_updater.h"

/// let's advertise myselft and this plugin ;)
#define ICQPROTONAME		"SAR"
#if defined( _UNICODE )
#define ICQPROTODECRSHORT   "Smart Auto Replier"
#else
#define ICQPROTODECRSHORT   "Smart Auto Replier"
#endif
#define ICQPROTODECR		"Plugin is able to reply on all incoming messages, making possible use of rules that are applied to specific contacts. Plugin allows to use Lua scripts as a rules, thus allowing user to make virtually any type of customizations."
#define DEVNAME				"Volodymyr M. Shcherbyna"
#define DEVMAIL				"volodymyr@shcherbyna.com"
#define DEVCOPYRIGHT		"� 2004-2012 Volodymyr M. Shcherbyna, www.shcherbyna.com"
#define DEVWWW				"http://www.shcherbyna.com/forum/viewforum.php?f=8"
#define GLOB_HOOKS			3

/// global menu items strings...
#define MENU_ITEM_DISABLE_CAPTION	TranslateTS(TEXT("Disable Smart Auto Replier"))
#define MENU_ITEM_ENABLE_CAPTION	TranslateTS(TEXT("Enable Smart Auto Replier"))

//#define 
/// global data...
HINSTANCE		hInst = NULL;				/// hinstance 
PLUGINLINK		*pluginLink = NULL;			/// ptr to plugin object
CMessagesHandler *g_pMessHandler = NULL;	/// ptr to "main" global manager object
CSettingsDlgHolder *g_pSettingsDlg = NULL;
static HANDLE g_hHook[GLOB_HOOKS] = {NULL};	/// global hooks
CLISTMENUITEM g_mi = {NULL};				/// handle to menu item that is used to switch on/off plugin
CLISTMENUITEM g_miAUR2This = {NULL};		/// handle to menu item that is used to add user to aur 
bool g_bEnableMItem = true;					/// flag for menu item..
HANDLE g_hMenuItem;							/// global handle to menu service
CCrushLog CRUSHLOGOBJ;						/// global object that handles all crushes
LPTSTR g_strPluginName = TEXT(ICQPROTONAME);		/// global string that represents plugin name...
INT	   g_nCurrentMode = 0;					/// current mode of a protocol == i duno which exactly protocol...
HANDLE g_hAUR2User = NULL;					/// handle to proto service of "autoreply to this user"

struct MM_INTERFACE mmi;

/// forming an info structure
PLUGININFOEX pluginInfo =
{
	sizeof(PLUGININFOEX),
	ICQPROTODECRSHORT,
	PLUGIN_MAKE_VERSION(2, 0, 0, 3),
	ICQPROTODECR,
	DEVNAME,
	DEVMAIL,
	DEVCOPYRIGHT,
	DEVWWW,
	UNICODE_AWARE,	
	0,
//#if defined( _UNICODE )
//	// {B9C9AC38-9D81-45D3-A9D7-67A7D8EA9D29} 
//	{ 0xb9c9ac38, 0x9d81, 0x45d3, { 0xa9, 0xd7, 0x67, 0xa7, 0xd8, 0xea, 0x9d, 0x29 } }
//#else
	// {9E536082-017E-423B-BF4F-DEDFEB9B3B60}
	{ 0x9e536082, 0x17e, 0x423b, { 0xbf, 0x4f, 0xde, 0xdf, 0xeb, 0x9b, 0x3b, 0x60 } }
//#endif
};

/// mapping into mirandaim.exe
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{	
BEGIN_PROTECT_AND_LOG_CODE
	hInst = hinstDLL;
	DisableThreadLibraryCalls(hinstDLL);
	return TRUE;
END_PROTECT_AND_LOG_CODE
}

__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
{	
	return &pluginInfo;
}

extern "C" __declspec(dllexport) const MUUID interfaces[] = {MIID_CHAT, MIID_SRMM, MIID_LAST};

__declspec(dllexport) const MUUID * MirandaPluginInterfaces(void)
{
	return interfaces;
}

/// handler of event when the options are choosed
static int OptionsInitialized(WPARAM wp, LPARAM lp)
{	
BEGIN_PROTECT_AND_LOG_CODE
	LPTSTR lpszGroup = TranslateTS(TEXT("Plugins"));
	OPTIONSDIALOGPAGE optsDialog = {0};
	optsDialog.cbSize = sizeof(OPTIONSDIALOGPAGE);
	optsDialog.hInstance = hInst;	
	optsDialog.position = 910000000;
	optsDialog.pszTemplate = (char*)MAKEINTRESOURCE(IDD_SDLGHOLDER);
	TCHAR szMax[MAX_PATH] = {0};
	_tcscpy(szMax, TranslateTS(TEXT(ICQPROTODECRSHORT)));
	
#ifdef _UNICODE
	optsDialog.ptszGroup = lpszGroup;
	optsDialog.ptszTitle = szMax;
#else
	optsDialog.pszTitle = (char*)szMax;
	optsDialog.pszGroup = lpszGroup;	
#endif
	optsDialog.pfnDlgProc = (DLGPROC)&CSettingsDlgHolder::FakeDlgProc;
	optsDialog.flags = ODPF_BOLDGROUPS/*|ODPF_EXPERTONLY*/; /// some lames are scaring that option...
#ifdef _UNICODE
	optsDialog.flags |= ODPF_UNICODE;
#endif
	
	g_pSettingsDlg->m_bDestroying = false;
	CallService(MS_OPT_ADDPAGE, wp, reinterpret_cast<LPARAM>(&optsDialog));
END_PROTECT_AND_LOG_CODE
	return FALSE;
}

/// pre building menu item for a contact..
static int AURContactPreBuildMenu(WPARAM wParam, LPARAM lParam)
{
	LPTSTR lpContactName = g_pMessHandler->GetContactName(reinterpret_cast<HANDLE>(wParam));
	CRulesStorage & storage = g_pMessHandler->getSettings().getStorage();
	bool bval = storage.RuleIsRegistered(lpContactName);

	if (bval)
	{
		g_miAUR2This.flags = CMIM_FLAGS | CMIF_GRAYED;
	}	
	else
	{
		g_miAUR2This.flags = CMIM_FLAGS;
	}

	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hAUR2User, (LPARAM)&g_miAUR2This);

	return 0;
}

/// handler of clicking on menu item
/// aur to this user...
static int AUR2User(WPARAM wParam, LPARAM lParam)
{
	CAddRuleDlg dlg;
	RULE_ITEM item;
	item.ContactName = g_pMessHandler->GetContactName(reinterpret_cast<HANDLE>(wParam));
	//item.ReplyAction = " ";
	item.ReplyText = SETTINGS_DEF_MESSAGE_RULE;
	TCHAR rulename[MAX_PATH * 5] = {0};
	_tcscat(rulename, TEXT("reply to "));
	_tcscat(rulename, item.ContactName);
	item.RuleName = rulename;

	dlg.m_baur2thisMode = true;
	dlg.m_item = item;
	dlg.DoModal();
	if (dlg.m_bAddedOk)
	{	/// already added...
		OnRefreshSettings();
	}

	return 0;
}

/// handler of clicking on menu item enablr/disable plugin...
static int PluginMenuCommand(WPARAM wParam, LPARAM lParam)
{
BEGIN_PROTECT_AND_LOG_CODE
	if (!g_pMessHandler)
		return 0;

	if (g_bEnableMItem)
	{/// lets handle enabling of AUR feature
		g_pMessHandler->MakeAction(true); /// this will enable plugin
	}
	else
	{/// lets handle disabling of AUR feature
		g_pMessHandler->MakeAction(false); /// this will disable plugin
	}

	/// put here stuff to do
	TCHAR sz[MAX_PATH] = {0};
	_tcscpy(sz, (g_bEnableMItem == true ? MENU_ITEM_DISABLE_CAPTION : MENU_ITEM_ENABLE_CAPTION));
	g_mi.pszName = (char*)TranslateTS(sz);
	g_mi.flags = CMIM_NAME;

#ifdef _UNICODE
	g_mi.flags |= CMIF_UNICODE;
#endif

#ifdef _DEBUG
	int nretval = 
#endif
	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)g_hMenuItem, (LPARAM)&g_mi);

	g_bEnableMItem = !g_bEnableMItem;
END_PROTECT_AND_LOG_CODE
	return 0;
}

/// handler that is invoked when mode is changed
static int OnStatusModeChanged(WPARAM wp, LPARAM lp)
{	
BEGIN_PROTECT_AND_LOG_CODE
	g_nCurrentMode = static_cast<INT>(wp);
	if (!g_pMessHandler)
		return FALSE;

	bool bNonOnOffLine = (wp != ID_STATUS_ONLINE &&
								  wp != ID_STATUS_OFFLINE &&
								  wp != ID_STATUS_FREECHAT);

	REPLYER_SETTINGS & s = g_pMessHandler->getSettings().getSettings();
	COMMON_RULE_ITEM & r = g_pMessHandler->getSettings().getStorage().getCommonRule();

	if (!bNonOnOffLine && s.bDisableWhenModeIsSet)
	{
		if (s.bEnabled)	/// plugin is enabled... we have a chance to disable it
		{
			bool bDisableIt = false;

			if (s.ModeDisValue == 0)
				bDisableIt = true;
			else if (s.ModeDisValue == 1 && wp == ID_STATUS_ONLINE)
				bDisableIt = true;
			else if (s.ModeDisValue == 2 && wp == ID_STATUS_FREECHAT)
				bDisableIt = true;

			if (bDisableIt)
			{	/// disable plugin...
				PluginMenuCommand(0, 0);
			}
		}
		return FALSE;
	}		

	if (s.bShowAURDlgWhenModeChanges)
	{
		CEditReplyDlg dlg;
		dlg.m_commRule = r;

		dlg.DoModal();
		if (dlg.m_bAllOk)
		{
			r = dlg.m_commRule;
		}
	}
	if (s.bEnableWhenModeIsSet)
	{
		if (!s.bEnabled)
		{			
			bool bEnable = false;
			if (s.ModeValue == 0 && bNonOnOffLine)
				bEnable = true;
			else if (s.ModeValue == 1 && wp == ID_STATUS_DND)
				bEnable = true;
			else if (s.ModeValue == 2 && wp == ID_STATUS_NA)
				bEnable = true;
			else if (s.ModeValue == 3 && wp == ID_STATUS_AWAY)
				bEnable = true;
			else if (s.ModeValue == 4 && wp == ID_STATUS_OCCUPIED)
				bEnable = true;

			if (bEnable) /// enabling plugin... (I realize that enabling and disabling is made via ass but i am lazy today)
				PluginMenuCommand(0, 0);
		}
	}

	return FALSE;
END_PROTECT_AND_LOG_CODE
	return FALSE;
}

#ifndef _DEBUG
LPTOP_LEVEL_EXCEPTION_FILTER UnhandledExceptionFilter(
  LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
)
{
	LPTOP_LEVEL_EXCEPTION_FILTER f = {0};
BEGIN_PROTECT_AND_LOG_CODE	
	throw f;	/// show msgbox to user and store inf to file	
END_PROTECT_AND_LOG_CODE
	return f;	/// let compiler shut ups...
}
#endif

int OnModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	Update update = {0}; // for c you'd use memset or ZeroMemory...
	char szVersion[16];

	update.cbSize = sizeof(Update);

	update.szComponentName	= pluginInfo.shortName;
	update.pbVersion		= (BYTE*)CreateVersionStringPluginEx(&pluginInfo, szVersion);
	update.cpbVersion		= strlen((char*)update.pbVersion);

	// these are the three lines that matter - the archive, the page containing the version string, and the text (or data) 
	// before the version that we use to locate it on the page
	// (note that if the update URL and the version URL point to standard file listing entries, the backend xml
	// data will be used to check for updates rather than the actual web page - this is not true for beta urls)
	//update.szUpdateURL		= "http://www.shcherbyna.com/files/sar/SAR.zip";
	//update.szVersionURL		= "http://www.shcherbyna.com/files/sar/SAR.html";

	update.szUpdateURL = "http://addons.miranda-im.org/details.php?action=viewfile&id=1609";
	
	//update.pbVersionPrefix	= (BYTE *)"SAR version ";
	
	//update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);

	// do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL

	CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);

	// Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
	// CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);	

	return 0;
}

/// loading plugin
extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
{
#ifndef _DEBUG
	SetUnhandledExceptionFilter(UnhandledExceptionFilter);
#endif
#ifdef _DEBUG
	//MessageBox (NULL, __FUNCTION__, __FILE__, MB_OK);
#endif	

	CCrushLog::Init(); /// crushes manager should be inited first
	int nRetVal = 0;
BEGIN_PROTECT_AND_LOG_CODE	
	pluginLink = link;

#ifndef _DEBUG
	/// creates help file near plugin
	/// if it is absent...
	/// note: originally help is in recources of plugin
	//CheckForHelpFile();
#endif
					 /// main manager...
	g_pMessHandler = new CMessagesHandler();
	g_pSettingsDlg = new CSettingsDlgHolder();

	if (g_pMessHandler)
	{	/// is inited
		REPLYER_SETTINGS & s = g_pMessHandler->getSettings().getSettings();
		if (s.bDisableWhenMirandaIsOn)
		{	
			s.bEnabled = false;
		}
		else
		{
			if (s.bEnabled)
				g_pMessHandler->HookEvents();
		}
		
		/// hook main events
		//HookEvent(MS_AWAYMSG_SHOWAWAYMSG, OnShow
		g_hHook[0] = HookEvent(ME_OPT_INITIALISE, OptionsInitialized);
		g_hHook[1] = HookEvent(ME_CLIST_STATUSMODECHANGE, OnStatusModeChanged);
		g_hHook[2] = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);

		/// Yushenko Tak, Yanukovich - Mudak. (Orange revolution)
		/// this was written in 2004-2005 in Kiev :)

		/// create menu item for disabling / enabling plugin
		CreateServiceFunction("AUR/MenuCommand", PluginMenuCommand);
		ZeroMemory(&g_mi, sizeof(g_mi));
		g_mi.cbSize = sizeof(g_mi);
		g_mi.position = -0x7FFFFFFF;
		g_mi.flags = 0;
		g_mi.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SAR_ICON)); //LoadSkinnedIcon(IDI_AUR_ICON/*SKINICON_OTHER_MIRANDA*/);
		bool bVal = g_pMessHandler->getSettings().getSettings().bEnabled;
		TCHAR sz[MAX_PATH] = {0};
		_tcscpy(sz, bVal ? MENU_ITEM_DISABLE_CAPTION : MENU_ITEM_ENABLE_CAPTION);
#ifdef _UNICODE
		g_mi.ptszName = TranslateTS(sz);
		g_mi.flags = CMIF_UNICODE;
#else
		g_mi.pszName = (char*)TranslateTS(sz);
#endif
		g_mi.pszService = "AUR/MenuCommand";

		g_hMenuItem = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&g_mi);
		g_bEnableMItem = !bVal;

		/// creating menu item for a contact		
		ZeroMemory(&g_miAUR2This, sizeof(g_miAUR2This));
		g_miAUR2This.cbSize = sizeof(g_miAUR2This);
		g_miAUR2This.position = -2000010000;
		g_miAUR2This.flags = 0;
		g_miAUR2This.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SAR_ICON));
		g_miAUR2This.pszContactOwner = NULL;
#ifdef _UNICODE
		g_miAUR2This.ptszName = TranslateTS(TEXT("&Smart Auto Reply to this user ..."));
		g_miAUR2This.flags = CMIF_UNICODE;
#else
		g_miAUR2This.pszName = TranslateTS(TEXT("&Smart Auto Reply to this user ..."));
#endif
		CreateServiceFunction("AUR/AURToThis", AUR2User);
		g_miAUR2This.pszService = "AUR/AURToThis";
		g_hAUR2User = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&g_miAUR2This);
		HookEvent(ME_CLIST_PREBUILDCONTACTMENU, AURContactPreBuildMenu);
	}

	CallService(MS_SYSTEM_GET_MMI, 0, (LPARAM)&mmi);

END_PROTECT_AND_LOG_CODE
	return FALSE;
}

/// unloading plugin
extern "C" int __declspec(dllexport) Unload(void)
{
BEGIN_PROTECT_AND_LOG_CODE
	for (size_t i = 0; i < GLOB_HOOKS; i++)
		UnhookEvent(g_hHook[i]);

	if (g_pSettingsDlg)
	{
		if (g_pSettingsDlg->IsWindow())
			g_pSettingsDlg->DestroyWindow();
		delete g_pSettingsDlg;
	}
	if (g_pMessHandler)
	{	/// unhook manager's events...
		g_pMessHandler->UnHookEvents();
		delete g_pMessHandler; /// call dtor
		g_pMessHandler = NULL;
	}	
END_PROTECT_AND_LOG_CODE

	CCrushLog::DeInit(); /// destroy crushes manager obj
	return FALSE;
}