/*
 * Miranda-IM Vypress Chat/quickChat plugins
 * Copyright (C) Saulius Menkevicius
 *
 * 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
 *
 * $Id: service.c,v 1.30 2005/03/15 02:03:47 bobas Exp $
 */

#include "miranda.h"
#include "libvqproto/vqproto.h"

#include "main.h"
#include "service.h"
#include "resource.h"
#include "user.h"
#include "userlist.h"
#include "contacts.h"
#include "options.h"
#include "chatroom.h"
#include "chanlist.h"
#include "util.h"
#include "msgloop.h"
#include "skin.h"

/* defines */
#define SVC_CONTACT_CHANSETTINGS "/ChanSettings"
#define SVC_CONTACT_BEEP	"/SendBeep"
#define SVC_MENU_SET_NICKNAME	"/SetNickname"
#define SVC_MENU_JOIN_CHANNEL	"/JoinChannel"

/* static data
 */
static HANDLE	s_hook_ModulesLoaded;
static HANDLE	s_hook_menuPreBuild;
static HANDLE	s_hook_contactDeleted;

static HANDLE	s_hContactMenuBeep, s_hContactChanSettings;
static HANDLE	s_hMenuJoinChannel, s_hMenuSetNickname;
static HWND	s_hwndJoinChannel, s_hwndSetNickname;
static char *	s_joinChannelChanlist = NULL;

static vqp_link_t s_vqpLink;

/* static routines
 */
static int
service_GetCaps(WPARAM wParam, LPARAM lParam)
{
	switch(wParam) {
	case PFLAGNUM_1:
		return PF1_IM | PF1_MODEMSG | PF1_BASICSEARCH;

	case PFLAGNUM_2:
		return PF2_ONLINE|PF2_SHORTAWAY|PF2_HEAVYDND|PF2_LONGAWAY;
	
	case PFLAGNUM_3:
		/* status modes where away can be set */
		return PF2_HEAVYDND|PF2_LONGAWAY;

	case PFLAG_UNIQUEIDTEXT:
		return (int)"Nickname";
		
	case PFLAG_UNIQUEIDSETTING:
		return (int)"Nick";

	case PFLAG_MAXLENOFMESSAGE:
		return VQP_MAX_PACKET_SIZE; /* an approximation */
	}

	return 0;
}

static int
service_GetName(WPARAM wParam, LPARAM lParam)
{
	strncpy((char*)lParam, VQCHAT_PROTO_NAME, wParam);
	return 0;
}

static int
service_LoadIcon(WPARAM wParam, LPARAM lParam)
{
	UINT id;

	switch(wParam) {
	case PLI_PROTOCOL|PLIF_LARGE:
		id = IDI_VQCHAT_PROTO_LARGE;
		break;
		
	case PLI_PROTOCOL|PLIF_SMALL:
		id = IDI_VQCHAT_PROTO;
		break;

	case PLI_ONLINE|PLIF_SMALL:
		id = IDI_VQCHAT_PROTO_ONLINE;
		break;

	case PLI_OFFLINE|PLIF_SMALL:
		id = IDI_VQCHAT_PROTO_OFFLINE;
		break;

	default:
		return 0;
	}

	return (int)LoadImage(
		g_hDllInstance, MAKEINTRESOURCE(id), IMAGE_ICON,
		GetSystemMetrics((wParam & PLIF_SMALL) ? SM_CXSMICON: SM_CXICON),
		GetSystemMetrics((wParam & PLIF_SMALL) ? SM_CYSMICON: SM_CYICON),
		0);
}

static int
service_SetStatus(WPARAM wParam, LPARAM lParam)
{
	int oldStatus = user_status();
		
	if(!user_set_status(wParam)) {
		ProtoBroadcastAck(
			VQCHAT_PROTO, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS,
			(HANDLE)oldStatus, (LPARAM)user_status());
	}

	return 0;
}

static int
service_GetStatus(WPARAM wParam, LPARAM lParam)
{
	return user_status();
}

static int
service_SetAwayMsg(WPARAM wParam, LPARAM lParam)
{
	/*int status_mode = wParam;*/
	LPCSTR szMessage = (LPCSTR)lParam;	/* note that szMessage can be NULL! */
	char * message;

	if(user_offline())
		return 1;

	/* convert to utf and set */
	message = util_loc2utf(szMessage ? szMessage: "");
	user_set_awaymsg(message);
	free(message);

	return 0;	/* success */
}

static void CALLBACK
service_BasicSearch_ResultAPC(ULONG_PTR dwParam)
{
	if(dwParam) {
		/* user found */
		PROTOSEARCHRESULT psr;

		memset(&psr, 0, sizeof(psr));
		psr.cbSize = sizeof(psr);
		psr.nick = (char*)dwParam;

		ProtoBroadcastAck(VQCHAT_PROTO, NULL, ACKTYPE_SEARCH,
			ACKRESULT_DATA, (HANDLE)1, (LPARAM)&psr);

		free((char*)dwParam);
	}

	/* search finished */
	ProtoBroadcastAck(VQCHAT_PROTO, NULL, ACKTYPE_SEARCH,
		ACKRESULT_SUCCESS, (HANDLE)1, 0);
}

/* service_BasicSearch:
 *	implement case insensitive search
 */
static int
service_BasicSearch(WPARAM wParam, LPARAM lParam)
{
	char * nickname;
	
	if(user_offline())
		return 0;

	nickname = util_loc2utf((char*)lParam);
	QueueUserAPC(
		service_BasicSearch_ResultAPC, g_hMainThread,
		(ULONG_PTR)(userlist_user_exists(nickname) ? strdup(nickname): 0));
	free(nickname);
	
	return 1;	/* search started (imaginary handle 1) */
}

static int
service_AddToList(WPARAM wParam, LPARAM lParam)
{
	int added;
	char * nickname;
	
	/* wParam = flags */
	PROTOSEARCHRESULT * psresult = (PROTOSEARCHRESULT*)lParam;

	if(user_offline())
		return 0;

	nickname = util_loc2utf(psresult->nick);
	added = (int)contacts_add_contact(nickname, 1);
	free(nickname);

	return added;
}

static int
service_GetContactInfo(WPARAM wParam, LPARAM lParam)
{
	CCSDATA * ccs = (CCSDATA*)lParam;

	if(user_offline())
		return 1;
	
	return contacts_get_contact_info(ccs->hContact, ccs->wParam);
}

static int
service_contact_send_message(WPARAM wParam, LPARAM lParam)
{
	CCSDATA * ccs = (CCSDATA*)lParam;
	char * message;
	int result;

	message = util_loc2utf((const char*)ccs->lParam);
	result = contacts_send_contact_message(ccs->hContact, message, wParam, 0);
	free(message);
	
	return result;
}

static int
service_contact_send_messageW(WPARAM wParam, LPARAM lParam)
{
	CCSDATA * ccs = (CCSDATA*)lParam;
	int result, len;
	char * message;

	len = strlen((const char *) ccs->lParam);
	message = util_uni2utf((wchar_t *)((const char *)ccs->lParam + len + 1));

	result = contacts_send_contact_message(ccs->hContact, message, wParam, 0);
	free(message);

	return result;
}

/* service_contact_received_message:
 *	invoked after our PSR_MESSAGE from contacts.c:contacts_input_contact_message()
 *	gets routed through all protocol plugins, and gets back to us
 */
static int
service_contact_received_message(WPARAM wParam, LPARAM lParam)
{
	CCSDATA * ccs = (CCSDATA *)lParam;
	PROTORECVEVENT * pre = (PROTORECVEVENT *)ccs->lParam;
	DBEVENTINFO dbei;

	memset(&dbei, 0, sizeof(dbei));
	dbei.cbSize = sizeof(dbei);
	dbei.szModule = VQCHAT_PROTO;
	dbei.timestamp = pre->timestamp;
	dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ: 0;
	dbei.eventType = EVENTTYPE_MESSAGE;
	dbei.cbBlob = strlen(pre->szMessage) + 1;
	if(pre->flags & PREF_UNICODE)
		dbei.cbBlob *= (sizeof(wchar_t) + 1);
	dbei.pBlob = (PBYTE) pre->szMessage;
	
	CallService(MS_DB_EVENT_ADD, (WPARAM)ccs->hContact, (LPARAM)&dbei);

	return 0;	/* success */
}

static int
service_contact_beep(WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact = (HANDLE)wParam;
	if(hContact && contacts_is_user_contact(hContact))
		contacts_send_beep(hContact);
	
	return 0;
}

static int
service_contact_channel_settings(WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact = (HANDLE)wParam;
	if(hContact && contacts_is_chatroom_contact(hContact)) {
		DBVARIANT dbv;
		if(!db_get(hContact, VQCHAT_PROTO, "Nick", &dbv)) {
			/* show channel settings (skip '#' at the beginning) */
			char * channel = util_loc2utf(dbv.pszVal + 1);
			chatroom_channel_show_settings_dlg(channel);

			free(channel);
			DBFreeVariant(&dbv);
		}
	}

	return 0;
}

static int
service_prebuild_contact_menu(WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact = (HANDLE)wParam;

	if(hContact) {
		CLISTMENUITEM clmi;
		memset(&clmi, 0, sizeof(clmi));
		clmi.cbSize = sizeof(clmi);

		/* beep */
		if(contacts_is_user_contact(hContact)) {
			clmi.flags = CMIM_FLAGS
				| (contacts_get_contact_status(hContact)
							==ID_STATUS_OFFLINE
					? CMIF_GRAYED: 0);
		} else {
			clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
		}
		CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hContactMenuBeep, (LPARAM)&clmi);

		/* channel settings */
		clmi.flags = CMIM_FLAGS
			| (!contacts_is_chatroom_contact(hContact)
							? CMIF_HIDDEN: 0);
		CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hContactChanSettings, (LPARAM)&clmi);
	}

	return 0;
}

static int
service_contact_deleted(WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact = (HANDLE)wParam;

	/* leave the channel */
	if(hContact!=NULL && contacts_is_chatroom_contact(hContact)) {
		DBVARIANT dbv;

		if(!db_get(hContact, VQCHAT_PROTO, "Nick", &dbv)) {
			/* channel name must begin with '#' */
			if(dbv.pszVal[0]=='#') {
				char * channel = util_loc2utf(dbv.pszVal + 1);
				chatroom_channel_part(channel, 0);
				free(channel);
			}
			
			DBFreeVariant(&dbv);
		}
	}
	
	return 0;
}

void service_join_channel_merge_chanlist(const char * user_chanlist)
{
	if(s_hwndJoinChannel) {
		char * chanlist = chanlist_copy(user_chanlist),
			* channel;

		while((channel = chanlist_shift(&chanlist)) != NULL) {
			if(!chanlist_contains(*user_p_chanlist(), channel)
					&& !chanlist_contains(s_joinChannelChanlist, channel)) {
				/* add '#'+channel to combo box list */
				char * mod_channel = malloc(strlen(channel) + 2);
				wchar_t * u_channel;
				mod_channel[0] = '#';
				strcpy(mod_channel + 1, channel);
				u_channel = util_utf2uni(mod_channel);
				free(mod_channel);

				SendMessageW(
					GetDlgItem(s_hwndJoinChannel, IDC_JC_COMBO_CHANNEL),
					CB_ADDSTRING, 0, (LPARAM)u_channel);
				free(u_channel);

				/* add channel to the list of already visible channels
				 * in the combo list
				 */
				s_joinChannelChanlist = chanlist_add(
								s_joinChannelChanlist, channel);
			}
			free(channel);
		}
	}
}

static BOOL CALLBACK service_join_channel_dlg_wndproc(
	HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	int len;
	char * channel;
	char * r_user_nickname;
	
	switch(nMsg) {
	case WM_INITDIALOG:
		/* set dialog icon */
		SendMessage(
			hwnd, WM_SETICON, ICON_SMALL,
			(LPARAM)LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CHATROOM)));

		/* clear channel list and send channel list req*/
		SendMessage(GetDlgItem(hwnd, IDC_JC_COMBO_CHANNEL), CB_RESETCONTENT, 0, 0);

		r_user_nickname = util_utf2vqp(user_codepage(), user_nickname());
		msgloop_send(vqp_msg_channel_list_req(s_vqpLink, r_user_nickname), 0);
		free(r_user_nickname);

		chanlist_free(s_joinChannelChanlist);
		s_joinChannelChanlist = NULL;

		/* set focus on current combo box control */
		SetFocus(GetDlgItem(hwnd, IDC_JC_COMBO_CHANNEL));

		/* disable ok button, as there is no text in combo box */
		EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
		break;

	case WM_COMMAND:
		switch(HIWORD(wParam)) {
		case BN_CLICKED:
			switch(LOWORD(wParam)) {
			case IDOK:
				/* join the specified channel */
				channel = util_GetDlgItemTextUtf(hwnd, IDC_JC_COMBO_CHANNEL);
				chatroom_channel_join(channel[0]=='#' ? channel + 1: channel, 0);
				free(channel);

				/* close the dialog */
				SendMessage(hwnd, WM_CLOSE, 0, 0);
				break;
			}
			break;
			
		case CBN_EDITCHANGE:
			switch(LOWORD(wParam)) {
			case IDC_JC_COMBO_CHANNEL:
				len = GetWindowTextLengthW(GetDlgItem(hwnd, IDC_JC_COMBO_CHANNEL));
				EnableWindow(GetDlgItem(hwnd, IDOK), len!=0);
				break;
			}
			break;
			
		case CBN_SELCHANGE:
			switch(LOWORD(wParam)) {
			case IDC_JC_COMBO_CHANNEL:
				/* enable the 'join' button after
				 * the user selects a channel from the list
				 */
				EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
				break;
			}
			break;
		}
		break;

	case WM_CLOSE:
		DestroyWindow(hwnd);
		s_hwndJoinChannel = NULL;

		chanlist_free(s_joinChannelChanlist);
		s_joinChannelChanlist = NULL;
		break;
	}
	return FALSE;
}

static int service_menu_join_channel(WPARAM wParam, LPARAM lParam)
{
	if(!s_hwndJoinChannel) {
		s_hwndJoinChannel = CreateDialog(
			g_hDllInstance, MAKEINTRESOURCE(IDD_JC), NULL,
			service_join_channel_dlg_wndproc
		);
		ShowWindow(s_hwndJoinChannel, SW_SHOW);
	}
	SetActiveWindow(s_hwndJoinChannel);

	return 0;
}

static BOOL CALLBACK
service_set_nickname_dlg_wndproc(
	HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	int len;
	char * nickname;
	
	switch(nMsg) {
	case WM_INITDIALOG:
		/* set dialog icon */
		SendMessage(
			hwnd, WM_SETICON, ICON_BIG,
			(LPARAM)(HICON)LoadImage(
				g_hDllInstance, MAKEINTRESOURCE(IDI_VQCHAT_PROTO_LARGE),
				IMAGE_ICON, GetSystemMetrics(SM_CXICON),
				GetSystemMetrics(SM_CYICON), LR_SHARED)
		);

		/* set current nickname */
		util_SetDlgItemTextUtf(hwnd, IDC_SN_EDIT_NICKNAME, user_nickname());

		/* select the entire nickname */
		SendMessage(
			GetDlgItem(hwnd, IDC_SN_EDIT_NICKNAME),
			EM_SETSEL, (WPARAM)0, (LPARAM)-1);
		SetFocus(GetDlgItem(hwnd, IDC_SN_EDIT_NICKNAME));
		break;

	case WM_COMMAND:
		switch(HIWORD(wParam)) {
		case BN_CLICKED:
			switch(LOWORD(wParam)) {
			case IDOK:
				/* set nickname */
				nickname = util_GetDlgItemTextUtf(hwnd, IDC_SN_EDIT_NICKNAME);
				user_set_nickname(nickname, 1);
				free(nickname);

				/* FALLTHROUGH */
		
			case IDCANCEL:
				SendMessage(hwnd, WM_CLOSE, 0, 0);
				break;
			}
			break;
			
		case EN_CHANGE:
			switch(LOWORD(wParam)) {
			case IDC_SN_EDIT_NICKNAME:
				/* disable the ok button, if we have
				 * an invalid nickname entered
				 */
				len = GetWindowTextLengthW(GetDlgItem(hwnd, IDC_SN_EDIT_NICKNAME));
				EnableWindow(GetDlgItem(hwnd, IDOK), len!=0);
				break;
			}
			break;
		}
		break;

	case WM_CLOSE:
		DestroyWindow(hwnd);
		s_hwndSetNickname = NULL;
		break;
	}
	return FALSE;
}

static int
service_menu_set_nickname(WPARAM wParam, LPARAM lParam)
{
	if(!s_hwndSetNickname) {
		s_hwndSetNickname = CreateDialog(
			g_hDllInstance, MAKEINTRESOURCE(IDD_SN), NULL,
			service_set_nickname_dlg_wndproc
		);
		ShowWindow(s_hwndSetNickname, SW_SHOW);
	}
	SetActiveWindow(s_hwndSetNickname);

	return 0;
}

static void
service_init_menus()
{
	CLISTMENUITEM mi;
	memset(&mi, 0, sizeof(mi));
	mi.cbSize = sizeof(mi);

	/* normal contacts: beep */
	mi.pszName = "Alert Beep";
	mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CONTACT_BELL));
	mi.pszService = VQCHAT_PROTO SVC_CONTACT_BEEP;
	mi.pszContactOwner = VQCHAT_PROTO;
	mi.popupPosition = 500090000;
	mi.pszPopupName = VQCHAT_PROTO_NAME;
	s_hContactMenuBeep = (HANDLE)CallService(
		MS_CLIST_ADDCONTACTMENUITEM, (WPARAM)0, (LPARAM)&mi);

	/* menu item: set nickname */
	mi.pszName = "Set &nickname";
	mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_VQCHAT_PROTO));
	mi.pszService = VQCHAT_PROTO SVC_MENU_SET_NICKNAME;
	mi.popupPosition = 500090000;
	mi.pszPopupName = VQCHAT_PROTO_NAME;
	s_hMenuSetNickname = (HANDLE)CallService(
		MS_CLIST_ADDMAINMENUITEM, (WPARAM)0, (LPARAM)&mi);
	s_hwndSetNickname = NULL;

	if(chatroom_module_installed()) {
		/* chatrooms: channel settings */
		mi.pszName = "Channel settings";
		mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CHATROOM));
		mi.pszService = VQCHAT_PROTO SVC_CONTACT_CHANSETTINGS;
		mi.pszContactOwner = VQCHAT_PROTO;
		mi.popupPosition = 500090000;
		mi.pszPopupName = VQCHAT_PROTO_NAME;
		s_hContactChanSettings = (HANDLE)CallService(
			MS_CLIST_ADDCONTACTMENUITEM, (WPARAM)0, (LPARAM)&mi);

		/* menu item: join channel */
		mi.pszName = "&Join a channel";
		mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CHATROOM));
		mi.pszService = VQCHAT_PROTO SVC_MENU_JOIN_CHANNEL;
		mi.popupPosition = 500090000;
		mi.pszPopupName = VQCHAT_PROTO_NAME;
		mi.flags = CMIM_FLAGS | CMIF_GRAYED;
		s_hMenuJoinChannel = (HANDLE)CallService(
			MS_CLIST_ADDMAINMENUITEM, (WPARAM)0, (LPARAM)&mi);
		s_hwndJoinChannel = NULL;
	}
}

static int
service_hook_modules_loaded(WPARAM wParam, LPARAM lParam)
{
	options_hook_modules_loaded();
	skin_hook_modules_loaded();
	user_hook_modules_loaded();
	contacts_hook_modules_loaded();
	chatroom_hook_modules_loaded();

	service_init_menus();

	s_hook_menuPreBuild = HookEvent(
			ME_CLIST_PREBUILDCONTACTMENU, service_prebuild_contact_menu);
	s_hook_contactDeleted = HookEvent(ME_DB_CONTACT_DELETED, service_contact_deleted);

	return 0;
}

/* exported routines
 */
void service_register_services()
{
	/* register protocol service funcs */
	CreateServiceFunction(VQCHAT_PROTO PS_GETCAPS, service_GetCaps);
	CreateServiceFunction(VQCHAT_PROTO PS_GETNAME, service_GetName);
	CreateServiceFunction(VQCHAT_PROTO PS_LOADICON, service_LoadIcon);
	CreateServiceFunction(VQCHAT_PROTO PS_SETSTATUS, service_SetStatus);
	CreateServiceFunction(VQCHAT_PROTO PS_GETSTATUS, service_GetStatus);
	CreateServiceFunction(VQCHAT_PROTO PS_SETAWAYMSG, service_SetAwayMsg);
	CreateServiceFunction(VQCHAT_PROTO PS_BASICSEARCH, service_BasicSearch);
	CreateServiceFunction(VQCHAT_PROTO PS_ADDTOLIST, service_AddToList);
	
	CreateServiceFunction(VQCHAT_PROTO PSS_GETINFO, service_GetContactInfo);

	CreateServiceFunction(VQCHAT_PROTO PSS_MESSAGE, service_contact_send_message);
	CreateServiceFunction(VQCHAT_PROTO PSS_MESSAGE"W", service_contact_send_messageW);
	CreateServiceFunction(VQCHAT_PROTO PSR_MESSAGE, service_contact_received_message);

	/* contact menu services */
	CreateServiceFunction(
		VQCHAT_PROTO SVC_CONTACT_BEEP,
		service_contact_beep
	);
	CreateServiceFunction(
		VQCHAT_PROTO SVC_CONTACT_CHANSETTINGS,
		service_contact_channel_settings);

	/* main menu services */
	CreateServiceFunction(
		VQCHAT_PROTO SVC_MENU_SET_NICKNAME,
		service_menu_set_nickname
	);
	CreateServiceFunction(
		VQCHAT_PROTO SVC_MENU_JOIN_CHANNEL,
		service_menu_join_channel);
}

void service_hook_all()
{
	s_hook_ModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, service_hook_modules_loaded);
}

void service_uninit()
{
	/* unhook service module's hooks */
	UnhookEvent(s_hook_ModulesLoaded);
}

void service_connected(vqp_link_t link)
{
	CLISTMENUITEM clmi;
	memset(&clmi, 0, sizeof(clmi));
	clmi.cbSize = sizeof(clmi);

	/* enable 'Join channel menu item' */
	clmi.flags = CMIM_FLAGS;
	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hMenuJoinChannel, (LPARAM)&clmi);

	/* set link */
	s_vqpLink = link;
}

void service_disconnected()
{
	CLISTMENUITEM clmi;
	memset(&clmi, 0, sizeof(clmi));
	clmi.cbSize = sizeof(clmi);

	/* disable 'Join channel menu item' */
	clmi.flags = CMIM_FLAGS | CMIF_GRAYED;
	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hMenuJoinChannel, (LPARAM)&clmi);
}