/*
** Amster - Hotlist
** Copyright © 2000 by 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <proto/dos.h>
#include <proto/socket.h>
#include <proto/utility.h>

#include <netdb.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <bsdsocket/socketbasetags.h>
#include <error.h>
#include <time.h>

#include <MUI/Lamp_mcc.h>
#include <MUI/NListview_mcc.h>
#include <MUI/textinput_mcc.h>

#include "include/config.h"
#include "include/gui.h"
#include "include/info.h"
#include "include/mui.h"
#include "include/hotlist.h"
#include "include/prefs.h"
#include "include/search.h"
#include "include/resultview.h"
#include "include/share.h"

#include "include/protos.h"
#include "amster_Cat.h"

/* Global variables */

BOOL HotlistChanged = FALSE;

int browse_count = 0;
int browse_state = 0;


/* Private prototypes */

MUIF HotlistDisplay(REG(a2) char **array, REG(a1) struct HotlistEntry *entry);
MUIF HotlistCompare(REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct NList_CompareMessage *ncm);
MUIF HotlistDestruct(REG(a2) APTR pool, REG(a1) struct HotlistEntry *entry);
void LoadHotlist(struct HotlistData *data);
void SaveHotlist(struct HotlistData *data);
void UploadHotlist(struct HotlistData *data);
struct HotlistEntry *GetHotlistEntry(struct HotlistData *data, char *nick);


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

	switch (msg->MethodID) {
		case OM_NEW:
			return(HotlistNew(cl, obj, (APTR)msg));
		case HOTLIST_LOAD:
			data = INST_DATA(cl, obj);
			LoadHotlist(data);
			return NULL;
		case HOTLIST_SAVE:
			data = INST_DATA(cl, obj);
			SaveHotlist(data);
			return NULL;
		case HOTLIST_UPLOAD:
			data = INST_DATA(cl, obj);
			UploadHotlist(data);
			return NULL;
		case HOTLIST_CLEAR:
			data = INST_DATA(cl, obj);
			DoMethod(data->LV_Search, RESULTVIEW_CLEAR, (((muimsg)msg)->arg1));
			return NULL;
		case HOTLIST_SIGNON:
			{
			long pos;
			struct HotlistEntry *entry;

			data = INST_DATA(cl, obj);
			if (entry = GetHotlistEntry(data, (char *)(((muimsg)msg)->arg1))) {
				entry->Online = 1;
				if ((int)(((muimsg)msg)->arg2) != entry->Speed) {
					entry->Speed = (int)(((muimsg)msg)->arg2);
					HotlistChanged = TRUE;
				}
				pos = MUIV_NList_GetPos_Start;
				DoMethod(data->LV_Hotlist, MUIM_NList_GetPos, entry, &pos);
				DoMethod(data->LV_Hotlist, MUIM_NList_Redraw, pos);
				DoMethod(data->LV_Hotlist, MUIM_NList_Sort);
			}
			return NULL;
			}
		case HOTLIST_SIGNOFF:
			{
			long pos;
			struct HotlistEntry *entry;

			data = INST_DATA(cl, obj);
			if (entry = GetHotlistEntry(data, (char *)(((muimsg)msg)->arg1))) {
				entry->Online = 0;
				pos = MUIV_NList_GetPos_Start;
				DoMethod(data->LV_Hotlist, MUIM_NList_GetPos, entry, &pos);
				DoMethod(data->LV_Hotlist, MUIM_NList_Redraw, pos);
				DoMethod(data->LV_Hotlist, MUIM_NList_Sort);
			}
			return NULL;
			}
		case HOTLIST_SIGNOFF_ALL:
			{
			int i;
			struct HotlistEntry *entry;

			data = INST_DATA(cl, obj);
			for (i=0; ; i++) {
				DoMethod(data->LV_Hotlist, MUIM_NList_GetEntry, i, &entry);
				if (!entry) break;
				entry->Online = 0;
				DoMethod(data->LV_Hotlist, MUIM_NList_Redraw, i);
			}
			return NULL;
			}
		case HOTLIST_ADD:
			{
			struct HotlistEntry *entry;
			char *buf;
			data = INST_DATA(cl, obj);

			if ((((muimsg)msg)->arg1)) buf = (char *)(((muimsg)msg)->arg1);
			else get(data->ST_Nick, MUIA_String_Contents, &buf);
			if (buf && buf[0] != '\0' && (entry = malloc(sizeof(struct HotlistEntry)))) {
				entry->Online = 0;
				entry->Speed = 0;
				entry->Nick = strdup(buf);

				DoMethod(data->LV_Hotlist, MUIM_NList_InsertSingle, entry, MUIV_NList_Insert_Sorted);
				if (gui_onlinestate == ONLINE) nap_sendbuf(NAPC_HOTLIST_ADD, entry->Nick);
				HotlistChanged = TRUE;
			}
			if (!(((muimsg)msg)->arg1)) set(data->ST_Nick, MUIA_String_Contents, "");
			}
			return NULL;
		case HOTLIST_REMOVE:
			{
			struct HotlistEntry *entry;
			data = INST_DATA(cl, obj);

			DoMethod(data->LV_Hotlist, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &entry);
			if (entry && gui_onlinestate == ONLINE) nap_sendbuf(NAPC_HOTLIST_REMOVE, entry->Nick);

			DoMethod(data->LV_Hotlist, MUIM_NList_Remove, MUIV_NList_Remove_Active);
			HotlistChanged = TRUE;
			return NULL;
			}
		case HOTLIST_BROWSE:
			{
			struct HotlistEntry *entry;
			long showresults;
			data = INST_DATA(cl, obj);

			get(data->TG_Results, MUIA_Selected, &showresults);
			if (!showresults) {
				set(data->LV_Search, MUIA_ShowMe, TRUE);
				set(data->TG_Results, MUIA_Selected, TRUE);
			}

			DoMethod(data->LV_Hotlist, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &entry);
			if (entry) DoMethod(data->LV_Search, RESULTVIEW_NICK, entry->Nick);

			return NULL;
			}
		case HOTLIST_NICK:
			{
			u_long tmp;
			data = INST_DATA(cl, obj);

			get(obj, MUIA_Window_Open, &tmp);
			if (!tmp) set(obj, MUIA_Window_Open, TRUE);
			/* Open the window if it's not already opened */

			get(data->TG_Results, MUIA_Selected, &tmp);
			if (!tmp) {
				set(data->LV_Search, MUIA_ShowMe, TRUE);
				set(data->TG_Results, MUIA_Selected, TRUE);
			}

			if ((char *)(((muimsg)msg)->arg1) == NULL) {
				GetAttr(MUIA_String_Contents, data->ST_Nick, &tmp);
				if (tmp && ((char *)tmp)[0] != '\0') DoMethod(data->LV_Search, RESULTVIEW_NICK, (char *)tmp);
			}
			else DoMethod(data->LV_Search, RESULTVIEW_NICK, (char *)(((muimsg)msg)->arg1));
			return NULL;
			}
		case HOTLIST_FOUND:
			data = INST_DATA(cl, obj);
			DoMethod(data->LV_Search, RESULTVIEW_FOUND, ((muimsg)msg)->arg1);
			return NULL;
		case HOTLIST_TOGGLE_RESULTS:
			{
			u_long tmp;
			data = INST_DATA(cl, obj);

			GetAttr(MUIA_Selected, data->TG_Results, &tmp);
			if (tmp)
				set(data->LV_Search, MUIA_ShowMe, TRUE);
			else
				set(data->LV_Search, MUIA_ShowMe, FALSE);
			return NULL;
			}
		case HOTLIST_TOGGLE:
			data = INST_DATA(cl, obj);
			DoMethod(data->LV_Search, RESULTVIEW_TOGGLE_ONE);
			return NULL;
	}

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


ULONG HotlistNew(struct IClass *cl, Object *obj, struct opSet *msg)
{
	static const struct Hook HotlistDispHook = { {NULL, NULL}, &HotlistDisplay,   NULL, NULL };
	static const struct Hook HotlistCompHook = { {NULL, NULL}, &HotlistCompare,   NULL, NULL };
	static const struct Hook HotlistDestHook = { {NULL, NULL}, &HotlistDestruct,  NULL, NULL };

	struct HotlistData *data;

	Object *LV_Hotlist, *LV_Search;
	Object *BT_Add, *BT_Remove;
	Object *ST_Nick, *BT_Browse;
	Object *TG_Results;

	gColFormat = "COL=1 BAR, COL=2 BAR, COL=3 BAR, COL=4 BAR, COL=5 BAR, COL=6 BAR";

	if (obj = (Object *)DoSuperNew(cl, obj,
		MUIA_HelpNode, "hotlist",
		MUIA_Window_Title, MSG_HOTLIST_TITLE,
		MUIA_Window_ID, MAKE_ID('H','O','T','L'),
		WindowContents, HGroup,
			Child, VGroup,
				MUIA_HorizWeight, 25,
				Child, NListviewObject,
					MUIA_NListview_NList, LV_Hotlist = NListObject,
						InputListFrame,
						MUIA_NList_ListBackground, MUII_ListBack,
						MUIA_NList_Title, TRUE,
						MUIA_NList_Format, "BAR,BAR,",
						MUIA_NList_DisplayHook, &HotlistDispHook,
						MUIA_NList_CompareHook2, &HotlistCompHook,
						MUIA_NList_DestructHook, &HotlistDestHook,
					End,
				End,
				Child, HGroup,
					Child, Label2(MSG_SEARCHUSER),
					Child, ST_Nick = TextinputObject,
						StringFrame,
                        MUIA_Textinput_Multiline, FALSE,
						MUIA_HorizWeight, 400,
						MUIA_String_MaxLen, 20,
						MUIA_CycleChain, 1,
						MUIA_ShortHelp, MSG_SEARCHUSER_HELP,
					End,
				End,
				Child, HGroup,
					Child, BT_Add    = SimpleButton(MSG_HOTLIST_ADD_GAD),
					Child, BT_Remove = SimpleButton(MSG_HOTLIST_REMOVE_GAD),
					Child, BT_Browse = SimpleButton(MSG_SEARCHUSER_GAD),
				End,
				Child, HGroup,
					Child, TextObject,
						MUIA_HorizWeight, 25,
						MUIA_Text_PreParse, "\33r",
						MUIA_Text_Contents, MSG_HOTLIST_RESULTS,
					End,
					Child, TG_Results = ImageObject,
						ButtonFrame,
						MUIA_HorizWeight, 10,
						MUIA_Background, MUII_ButtonBack,
						MUIA_InputMode, MUIV_InputMode_Toggle,
						MUIA_ShowSelState, FALSE,
						MUIA_Image_Spec, "6:15",
						MUIA_Selected, FALSE,
					End,
				End,
			End,
			Child, BalanceObject, End,
			Child, LV_Search = NewObject(gui->resultview_mcc->mcc_Class, NULL, MUIA_Amster_ContextMenu, FALSE, TAG_DONE),
		End,
		TAG_MORE, msg->ops_AttrList))
	{
		data = INST_DATA(cl, obj);
		data->LV_Hotlist = LV_Hotlist;
		data->LV_Search  = LV_Search;
		data->TG_Results = TG_Results;
		data->ST_Nick    = ST_Nick;

		lamp_useinlist(LV_Hotlist);

		DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, obj, 3, MUIM_Set, MUIA_Window_Open, FALSE);

		DoMethod(BT_Add,    MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, HOTLIST_ADD);
		DoMethod(BT_Remove, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, HOTLIST_REMOVE);
		DoMethod(BT_Browse, MUIM_Notify, MUIA_Pressed, FALSE, obj, 2, HOTLIST_NICK, NULL);
		DoMethod(ST_Nick,   MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, obj, 2, HOTLIST_NICK, NULL);

		DoMethod(LV_Hotlist, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, obj, 1, HOTLIST_BROWSE);
		DoMethod(TG_Results, MUIM_Notify, MUIA_Selected, MUIV_EveryTime, obj, 1, HOTLIST_TOGGLE_RESULTS);

		LoadHotlist(data);

		/* Default sort */

		DoMethod(LV_Hotlist, MUIM_Set, MUIA_NList_TitleMark, 1);
		DoMethod(LV_Hotlist, MUIM_NList_Sort3, 1, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_Both);

		DoMethod(LV_Hotlist, MUIM_Notify, MUIA_NList_TitleClick,  MUIV_EveryTime, LV_Hotlist, 4, MUIM_NList_Sort3, MUIV_TriggerValue, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_Both);
		DoMethod(LV_Hotlist, MUIM_Notify, MUIA_NList_TitleClick2, MUIV_EveryTime, LV_Hotlist, 4, MUIM_NList_Sort3, MUIV_TriggerValue, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_2);
		DoMethod(LV_Hotlist, MUIM_Notify, MUIA_NList_SortType,    MUIV_EveryTime, LV_Hotlist, 3, MUIM_Set, MUIA_NList_TitleMark,  MUIV_TriggerValue);
		DoMethod(LV_Hotlist, MUIM_Notify, MUIA_NList_SortType2,   MUIV_EveryTime, LV_Hotlist, 3, MUIM_Set, MUIA_NList_TitleMark2, MUIV_TriggerValue);

		set(data->LV_Search, MUIA_ShowMe, FALSE);

		return((ULONG)obj);
	}
	return(0);
}


MUIF HotlistDisplay(REG(a2) char **array, REG(a1) struct HotlistEntry *entry)
{
	if (entry) {
		*array++ = entry->Nick;
		if (entry->Online) *array++ = "\33bOnline";
		else *array++ = "Offline";
/*
		if (entry->Online) *array++ = lamp_getforlist(4);
		else *array++ = lamp_getforlist(2);
*/
		*array = nap_linktype[entry->Speed];
	}
	else {
		*array++ = (char *)MSG_HOTLIST_NICK;
		*array++ = (char *)MSG_HOTLIST_STATUS;
		*array   = (char *)MSG_HOTLIST_LINK;
	}

	return NULL;
}


MUIF HotlistCompare(REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct NList_CompareMessage *ncm)
{
	struct HotlistEntry *entry1 = ncm->entry1;
	struct HotlistEntry *entry2 = ncm->entry2;
	LONG col1 = ncm->sort_type & MUIV_NList_TitleMark_ColMask;
	LONG col2 = ncm->sort_type2 & MUIV_NList_TitleMark2_ColMask;
	ULONG result = 0;

	if (ncm->sort_type == MUIV_NList_SortType_None) return (0);

	if (col1 == 0) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = (LONG) stricmp(entry2->Nick, entry1->Nick);
		else
			result = (LONG) stricmp(entry1->Nick, entry2->Nick);
	}
	else if (col1 == 1) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = entry1->Online - entry2->Online;
		else
			result = entry2->Online - entry1->Online;
	}
	else if (col1 == 2) {
		if (ncm->sort_type & MUIV_NList_TitleMark_TypeMask)
			result = entry2->Speed - entry1->Speed;
		else
			result = entry1->Speed - entry2->Speed;
	}

	if ((result != 0) || (col1 == col2)) return (result);

	if (col2 == 0) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = (LONG) stricmp(entry2->Nick, entry1->Nick);
		else
			result = (LONG) stricmp(entry1->Nick, entry2->Nick);
	}
	else if (col2 == 1) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = entry1->Online - entry2->Online;
		else
			result = entry2->Online - entry1->Online;
	}
	else if (col2 == 2) {
		if (ncm->sort_type & MUIV_NList_TitleMark2_TypeMask)
			result = entry2->Speed - entry1->Speed;
		else
			result = entry1->Speed - entry2->Speed;
	}

	return (result);
}


MUIF HotlistDestruct(REG(a2) APTR pool, REG(a1) struct HotlistEntry *entry)
{
	free(entry);
	return NULL;
}


void LoadHotlist(struct HotlistData *data)
{
	BPTR fh;
	char buf[1024];
	int line = 0;
	struct HotlistEntry *entry;
	LONG argarray[] = { NULL, NULL };
	UBYTE *argstr = "NICK/K/A,SPEED/K/N";
	struct RDArgs *rdargs;

	if (fh = Open("PROGDIR:Amster.hotlist", MODE_OLDFILE)) {
		set(data->LV_Hotlist, MUIA_NList_Quiet, MUIV_NList_Quiet_Full);
		if (rdargs = AllocDosObject(DOS_RDARGS, NULL)) {
			while (FGets(fh, buf, sizeof(buf))) {
				line++;

				rdargs->RDA_Buffer = NULL;
				rdargs->RDA_Source.CS_Buffer = buf;
				rdargs->RDA_Source.CS_Length = strlen(buf);
				rdargs->RDA_Source.CS_CurChr = 0;
				argarray[1] = NULL;

				if (ReadArgs(argstr, argarray, rdargs)) {
					if (entry = malloc(sizeof(struct HotlistEntry))) {
						entry->Nick = strdup((char *)argarray[0]);
						if (argarray[1]) entry->Speed = *((long *)argarray[1]);
						else entry->Speed = 0;
						entry->Online = 0;
						DoMethod(data->LV_Hotlist, MUIM_NList_InsertSingle, entry, TAG_DONE);

						if (gui_onlinestate == ONLINE) nap_sendbuf(NAPC_HOTLIST_INIT, entry->Nick);
					}
					FreeArgs(rdargs);
				}
				else gui_debugf((char *)MSG_PARSE_ERROR, "PROGDIR:Amster.hotlist", line);
			}
			FreeDosObject(DOS_RDARGS, rdargs);
		}
		set(data->LV_Hotlist, MUIA_NList_Quiet, MUIV_NList_Quiet_None);
		Close(fh);
		DoMethod(data->LV_Hotlist, MUIM_NList_Sort);
	}
}


void SaveHotlist(struct HotlistData *data)
{
	struct HotlistEntry *entry;
	BPTR fh;
	char buf[1024];
	int i;

	fh = Open("PROGDIR:Amster.hotlist", MODE_NEWFILE);
	if (!fh) return;

	for (i=0; ; i++) {
		DoMethod(data->LV_Hotlist, MUIM_NList_GetEntry, i, &entry);
		if (!entry) break;
		sprintf(buf, "NICK \"%s\" SPEED %d\n", entry->Nick, entry->Speed);
		Write(fh, buf, strlen(buf)); /* Should be buffered, check FWrite */
	}

	Close(fh);
	HotlistChanged = FALSE;
}


void UploadHotlist(struct HotlistData *data)
{
	struct HotlistEntry *entry;
	int i;

	if (gui_onlinestate < LOGGING_IN) return;

	for (i=0; ; i++) {
		DoMethod(data->LV_Hotlist, MUIM_NList_GetEntry, i, &entry);
		if (!entry) break;
		nap_sendbuf(NAPC_HOTLIST_INIT, entry->Nick);;
	}
}


void HotlistInterpret(u_int com, char *data)
{
	char *nick;
	int speed;

	switch(com) {
		case NAPC_USER_SIGNON:
			nick = nap_token(&data);
			speed = atoi(data);
			DoMethod(gui->WI_Hotlist, HOTLIST_SIGNON, nick, speed);
			break;
		case NAPC_USER_SIGNOFF:
			DoMethod(gui->WI_Hotlist, HOTLIST_SIGNOFF, data);
			break;
		case NAPC_HOTLIST_ACK:
			break;
		case NAPC_HOTLIST_ERROR:
			break;
	}
}


struct HotlistEntry *GetHotlistEntry(struct HotlistData *data, char *nick)
{
	struct HotlistEntry *entry;
	int i;

	for (i=0; ; i++) {
		DoMethod(data->LV_Hotlist, MUIM_NList_GetEntry, i, &entry);
		if (!entry) return NULL;
		if (stricmp(entry->Nick, nick) == 0) break;
	}

	return entry;
}
