/*
** Amster - Chat
** Copyright © 1999-2000 by Gürer Özen & Jacob Laursen
**
** Maintained by Jacob Laursen <laursen@myself.com>
**
** 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 "include/config.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "include/mui.h"
#include <MUI/NListview_mcc.h>
#include <MUI/textinput_mcc.h>

#include "include/gui.h"
#include "include/msg.h"
#include "include/channellist.h"
#include "include/chat.h"
#include "include/chatline.h"
#include "include/panel.h"
#include "include/prefs.h"
#include "include/search.h"
#include "include/napster.h"
#include "include/resultview.h"
#include "include/protos.h"
#include "amster_Cat.h"

channel chat_chans = NULL;

ULONG chat_new(struct IClass *cl, Object *obj, struct opSet *msg);
MUIF chat_msgdisp(REG(a2) char **array, REG(a1) struct ChatMessage *m);
MUIF userlistdisp(REG(a2) char **array, REG(a1) struct ChannelUserData *cud);
MUIF userlistdest(REG(a2) APTR pool, REG(a1) struct ChannelUserData *cud);
void channel_part(struct chatdata *data);
void chat_say(struct chatdata *data, char *user, char *msg);
void chat_sayf(struct chatdata *data, char *user, char *msg, ...);
void chat_saysys(struct chatdata *data, char *msg);
void chat_echo(struct chatdata *data, char *msg);
void chat_command(struct chatdata *data);


MUIF chat_dispatch(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
{
	struct chatdata *data;

	switch(msg->MethodID) {
		case OM_NEW:
			return(chat_new(cl, obj, (APTR)msg));
		case OM_DISPOSE:
			{
			channel c = chat_chans;
			data = INST_DATA(cl, obj);

			/* Update the linked list of channels */

			if (c == &data->chn) {
				chat_chans = c->next;
				/* The one to remove was the first in list,
				   so we just point to the next one */
			}
			else while (c) {
				if (c->next == &data->chn)
					c->next = data->chn.next;
					/* The next one is the one to remove, so we point to the next
					   one to that */
				c = c->next;
			}

			break;
			}
		case CHAT_COMMAND:
			data = INST_DATA(cl, obj);
			chat_command(data);
			return(0);
		case CHAT_JOINCHANNEL:
			data = INST_DATA(cl, obj);
			if (gui_onlinestate < ONLINE) chat_saysys(data, (char *)MSG_CHAT_NOTCONNECTED);
			else {
				data->chn.name = strdup((char *)(((muimsg)msg)->arg1));
				nap_sendbuf(NAPC_CHANNELJOIN, data->chn.name);
				data->chn.joinflag = 1;
			}
			return(0);
		case CHAT_JOINACK:
			data = INST_DATA(cl, obj);
			data->chn.joinflag = 2;
			DoMethod(data->rect, MUIM_Group_InitChange);
			set(data->chantxt, MUIA_Text_Contents, data->chn.name);
			DoMethod(data->rect, MUIM_Group_ExitChange);
			chat_sayf(data, "\33b-->", (char *)MSG_CHAT_JOINED_YOU, data->chn.name);
			return(0);
		case CHAT_PUBLICMSGRECV:
			data = INST_DATA(cl, obj);
			if (stricmp(prf->user, (char *)(((muimsg)msg)->arg1)) != 0)
				chat_say(data, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2));
				/* Only show messages from others */
			return(0);
		case CHAT_JOINMSG:
			data = INST_DATA(cl, obj);
			chat_sayf(data, "\33b-->", (char *)MSG_CHAT_JOINED_THIS, (char *)(((muimsg)msg)->arg1), (int)(((muimsg)msg)->arg2), nap_linktype[(int)(((muimsg)msg)->arg3)]);
		case CHAT_USERLIST:
			{
			struct ChannelUserData *ud;
			data = INST_DATA(cl, obj);
			if (ud = malloc(sizeof(struct ChannelUserData))) {
				memset(ud, 0, sizeof(struct ChannelUserData));
				ud->Name = strdup((char *)(((muimsg)msg)->arg1));
				ud->Shares = (int)(((muimsg)msg)->arg2);
				ud->Link = (int)(((muimsg)msg)->arg3);
				DoMethod(data->userlist, MUIM_NList_InsertSingle, ud, MUIV_NList_Insert_Bottom);
			}
			return(0);
			}
		case CHAT_USERPART:
			{
			int i;
			struct ChannelUserData *cud;
			data = INST_DATA(cl, obj);

			chat_sayf(data,"\33b<--", (char *)MSG_CHAT_PART_THIS, (char *)(((muimsg)msg)->arg1));

			for (i=0; ; i++) {
				DoMethod(data->userlist, MUIM_NList_GetEntry, i, &cud);
				if (!cud) break;
				if (strcmp((char *)(((muimsg)msg)->arg1), cud->Name) == 0) {
					DoMethod(data->userlist, MUIM_NList_Remove, i);
					break;
				}
			}

			return(0);
			}
		case CHAT_CHANNELPART:
			data = INST_DATA(cl, obj);
			if (data->chn.name) nap_sendbuf(NAPC_CHANNELPART, data->chn.name);
			DoMethod(gui->iconpanel, PANEL_DISPOSECHAT, obj);
			return(0);
		case CHAT_CHANNELTOPIC:
			data = INST_DATA(cl, obj);
			set(data->topicstr, MUIA_String_Contents, ((muimsg)msg)->arg1);
			return(0);

	}
	return(DoSuperMethodA(cl, obj, msg));
}


ULONG chat_new(struct IClass *cl, Object *obj, struct opSet *msg)
{
	static struct Hook msgdispHook      = { {0,0}, &chat_msgdisp, NULL, NULL };
	static struct Hook userlistdispHook = { {0,0}, &userlistdisp, NULL, NULL };
	static struct Hook userlistdestHook = { {0,0}, &userlistdest, NULL, NULL };

	struct chatdata *data;
	Object *rect, *chantxt, *topicstr, *msglist, *userlist, *msgstr;

	obj = (Object *)DoSuperNew(cl, obj,
		MUIA_HelpNode, "chat",
		MUIA_Window_Title, MSG_CHAT_TITLE,
		MUIA_Window_ID, MAKE_ID('C','H','A','T'),
		WindowContents, VGroup,
			Child, rect = HGroup,
				Child, chantxt = TextObject,
					MUIA_HorizWeight, 0,
					MUIA_Text_Contents, MSG_CHAT_NONEJOINED,
				End,
				Child, topicstr = TextinputObject,
					StringFrame,
                    MUIA_Textinput_Multiline, FALSE,
					MUIA_CycleChain, 1,
				End,
			End,
			Child, HGroup,
				Child, NListviewObject,
					MUIA_HorizWeight, 75,
					MUIA_NListview_NList, msglist = NListObject,
						ReadListFrame,
						MUIA_NList_Input, FALSE,
						MUIA_NList_Format, "BAR P=\33r,",
						MUIA_NList_DisplayHook, &msgdispHook,
						MUIA_NList_DefaultObjectOnClick, FALSE,
						MUIA_NList_AutoVisible, TRUE,
						MUIA_NList_AutoCopyToClip, TRUE,
						MUIA_NList_TypeSelect, MUIV_NList_TypeSelect_Char,
					End,
				End,
				Child, BalanceObject, End,
				Child, NListviewObject,
					MUIA_HorizWeight, 25,
					MUIA_NListview_NList, userlist = NListObject,
						InputListFrame,
						MUIA_NList_DefaultObjectOnClick, FALSE,
						MUIA_NList_Format, "",
						MUIA_NList_DisplayHook, &userlistdispHook,
						MUIA_NList_DestructHook, &userlistdestHook,
						MUIA_CycleChain, 1,
					End,
				End,
			End,
			Child, msgstr = TextinputObject,
				StringFrame,
				MUIA_CycleChain, 1,
			End,
		End,
		TAG_MORE, msg->ops_AttrList);

	if (!obj) return(0);

	data = INST_DATA(cl,obj);
	data->rect = rect;
	data->chantxt = chantxt;
	data->topicstr = topicstr;
	data->msglist = msglist;
	data->userlist = userlist;
	data->msgstr = msgstr;

	data->chn.next = chat_chans;
	chat_chans = &data->chn;
	data->chn.joinflag = 0;
	data->chn.name = NULL;
	data->chn.win = obj;

	DoMethod(msgstr, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, obj, 1, CHAT_COMMAND);
	DoMethod(msgstr, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, obj, 3, MUIM_Set, MUIA_Window_ActiveObject, msgstr);

	DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, MUIV_Notify_Application, 5, MUIM_Application_PushMethod, obj, 1, CHAT_CHANNELPART);

	return((ULONG)obj);
}


MUIF chat_msgdisp(REG(a2) char **array, REG(a1) struct ChatMessage *m)
{
	*array++ = m->Nick;
	*array = m->Msg;

	return(0);
}


MUIF userlistdisp(REG(a2) char **array, REG(a1) struct ChannelUserData *cud)
{
	static char line[64];

	sprintf(line, "%s (%ld)", cud->Name, cud->Shares);
	cud->Display = strdup(line);
	*array++ = cud->Display;
	*array = "";

	return(0);
}


MUIF userlistdest(REG(a2) APTR pool, REG(a1) struct ChannelUserData *cud)
{
	free(cud->Name);
	free(cud->Display);
	free(cud);

	return(0);
}

void chat_say(struct chatdata *data, char *user, char *msg)
{
	struct ChatMessage *m;

	m = malloc(sizeof(struct ChatMessage));
	if (!m) return;

	m->Nick = strdup(user);
	m->Msg = strdup(msg);

	DoMethod(data->msglist, MUIM_NList_InsertSingleWrap, m, MUIV_NList_Insert_Bottom, 2, NULL);
	set(data->msglist, MUIA_NList_First, MUIV_NList_Active_Bottom);
}


void chat_sayf(struct chatdata *data, char *user, char *msg, ...)
{
	static char buf[1024];
	va_list ap;

	va_start(ap, msg);
	vsprintf(buf, msg, ap);
	va_end(ap);
	chat_say(data, user, buf);
}


void chat_saysys(struct chatdata *data, char *msg)
{
	chat_say(data,"\33b***",msg);
}


void chat_echo(struct chatdata *data, char *msg)
{
	chat_say(data,"\33b»",msg);
}


void chat_command(struct chatdata *data)
{
	u_long tmp;
	char *com;

	GetAttr(MUIA_String_Contents, data->msgstr, &tmp);
	com = (char *)tmp;

	DoMethod(data->msgstr, CHATLINE_ADD, com);

	if (com[0] == '/') {
		if (strnicmp("join", com+1, 4) == 0 && strlen(com)>6) {
			if (gui_onlinestate < ONLINE) chat_saysys(data, (char *)MSG_CHAT_NOTCONNECTED);
			else {
				if (data->chn.name) channel_part(data);
				/* Leave channel in this window before joining a new one. */

				data->chn.name = strdup(com+6);
				nap_sendbuf(NAPC_CHANNELJOIN, data->chn.name);
				data->chn.joinflag = 1;
			}
		}
		else if (stricmp("leave", com+1) == 0) {
			if (gui_onlinestate < ONLINE) chat_saysys(data, (char *)MSG_CHAT_NOTCONNECTED);
			else channel_part(data);
		}
		else if (stricmp("list", com+1) == 0) {
			DoMethod(gui->WI_ChannelList, MUIM_Set, MUIA_Window_Open, TRUE);
			if (gui_onlinestate == ONLINE) DoMethod(gui->WI_ChannelList, CHANLIST_UPDATE);
		}

	}
	else {
		if (gui_onlinestate < ONLINE) chat_saysys(data, (char *)MSG_CHAT_NOTCONNECTED);
		else {
			if (data->chn.joinflag>1) {
				chat_say(data, prf->user, com);
				sprintf(nap_buf,"%s %s", data->chn.name, com);
				nap_send(NAPC_PUBLICMSGSEND);
			}
			else {
				chat_saysys(data, (char *)MSG_CHAT_NOTJOINED);
			}
		}
	}

	set(data->msgstr, MUIA_String_Contents, NULL);
}


void channel_part(struct chatdata *data)
{
	u_long item;

	nap_sendbuf(NAPC_CHANNELPART, data->chn.name);
	chat_sayf(data,"\33b<--", (char *)MSG_CHAT_PART_YOU, data->chn.name);

	set(data->userlist, MUIA_NList_Quiet, MUIV_NList_Quiet_Visual);
	while (1) {
		DoMethod(data->userlist, MUIM_NList_GetEntry, 0, &item);
		if (!item) break;
		DoMethod(data->userlist, MUIM_NList_Remove, MUIV_NList_Remove_First);
	}
	set(data->userlist, MUIA_NList_Quiet, MUIV_NList_Quiet_None);
	/* Clear users from list */

	data->chn.name = NULL;
	/* Clear channel name */

	set(data->chantxt, MUIA_Text_Contents, MSG_CHAT_NONEJOINED);
	/* Set channel name */
}


void chat_interpret(u_int com, char *data)
{
	channel c;

	switch (com) {
		case NAPC_PUBLICMSGRECV:
			c = chat_findchan(nap_token(&data));
			if (!c) break;
			DoMethod(c->win,CHAT_PUBLICMSGRECV,nap_token(&data),data);
			break;
		case NAPC_JOINACK:
			c = chat_findchan(data);
			if (!c) break;
			DoMethod(c->win,CHAT_JOINACK);
			break;
		case NAPC_USERPART:
			c = chat_findchan(nap_token(&data));
			if (!c) break;
			DoMethod(c->win,CHAT_USERPART,nap_token(&data));
			break;
		case NAPC_JOINMSG:
			c = chat_findchan(nap_token(&data));
			if (!c) break;
			DoMethod(c->win, CHAT_JOINMSG, nap_token(&data), nap_itoken(&data), atoi(data));
			break;
		case NAPC_USERLIST:
			c = chat_findchan(nap_token(&data));
			if (!c) break;
			DoMethod(c->win, CHAT_USERLIST, nap_token(&data), nap_itoken(&data), atoi(data));
			break;
		case NAPC_USERLISTEND:
			break;
		case NAPC_CHANNELTOPIC:
			c = chat_findchan(nap_token(&data));
			if (!c) break;
			DoMethod(c->win, CHAT_CHANNELTOPIC, data);
			break;
	}
}


channel chat_findchan(char *name)
{
	channel c = chat_chans;

	while (c) {
		if (stricmp(c->name, name) == 0) return(c);
		c = c->next;
	}
	return(0);
}
