/*
** Download
*/

#include "config.h"

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

#include <proto/exec.h>
#include <proto/socket.h>
#include <proto/dos.h>
#include <libraries/dos.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 "mui.h"
#include <MUI/NListview_mcc.h>
#include <MUI/NFloattext_mcc.h>
#include "gui.h"
#include "info.h"
#include "download.h"
#include "napster.h"
#include "prefs.h"
#include "amster_Cat.h"

dldata dl_list=NULL;

ULONG dl_new(struct IClass *cl, Object *obj, struct opSet *msg);
void _dl_pollq(dldata song);
void _dl_abort(dldata song, char *msg);
void _dl_filename(char *buf, char *title);

extern struct Hook handleHook;


ULONG __stdargs DoSuperNew(struct IClass *cl,Object *obj,ULONG tag1,...)
{
	return(DoSuperMethod(cl,obj,OM_NEW,&tag1,NULL));
}


MUIF dl_dispatch(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
{
	switch(msg->MethodID) {
		case OM_NEW: return(dl_new(cl,obj,(APTR)msg));
		case DL_ABORT:
			{
			struct dl_data *data = INST_DATA(cl,obj);
			_dl_abort(data->song,(char *)MSG_DL_ABORT);
			return(NULL);
			}
		case DL_CLOSE:
			{
			struct dl_data *data = INST_DATA(cl,obj);
			_dl_abort(data->song,(char *)MSG_DL_ABORT);
			data->song->abort=1;
			DoMethod(gui->app,MUIM_Application_PushMethod,gui->app,3,MUIM_CallHook,&handleHook,99);
			return(NULL);
			}
		case DL_GAUGE:
			{
			struct dl_data *data = INST_DATA(cl,obj);
			u_long i = (u_long)((muimsg)msg)->arg1;
			set(data->lvl,MUIA_Gauge_Divide,i/10000);
			return(NULL);
			}
		case DL_UPDATE:
			{
			static char buf[50];
			struct dl_data *data = INST_DATA(cl,obj);
			int i = (int)((muimsg)msg)->arg1;
			set(data->lvl,MUIA_Gauge_Current,i);
			sprintf(buf,"%ld",i);
			set(data->sz,MUIA_Text_Contents,buf);
			return(NULL);
			}
		case DL_STAT:
			{
			struct dl_data *data = INST_DATA(cl,obj);
			char *tmp = (char*)((muimsg)msg)->arg1;
			set(data->stat,MUIA_Text_Contents,tmp);
			return(NULL);
			}
	}
	return(DoSuperMethodA(cl,obj,msg));
}


ULONG dl_new(struct IClass *cl, Object *obj, struct opSet *msg)
{
	struct dl_data *data;
	dldata song = (dldata)GetTagData(DL_SONG,0,msg->ops_AttrList);
	Object *but,*stat,*lvl,*sz;

	if (obj = (Object *)DoSuperNew(cl,obj,
		WindowContents, VGroup,
			Child, NListviewObject,
				MUIA_NListview_NList, NFloattextObject,
					ReadListFrame,
					MUIA_NFloattext_Text,song->info,
				End,
			End,
			Child, stat = TextObject,
				TextFrame,
				MUIA_Background, MUII_TextBack,
				MUIA_Text_PreParse, "\33c",
				MUIA_Text_Contents, MSG_DL_HOSTLOOKUP,
			End,
			Child, lvl = GaugeObject,
				GaugeFrame,
				MUIA_Gauge_Horiz, TRUE,
				MUIA_Gauge_Max, 10000,
				MUIA_Gauge_Divide, 400,
				MUIA_Gauge_InfoText, "",
			End,
			Child, ScaleObject, End,
			Child, HGroup,
				Child, sz = TextObject,
					MUIA_Text_PreParse, "\33c",
				End,
				Child, but = SimpleButton(MSG_DL_ABORT_GAD),
			End,
		End,
		TAG_MORE, msg->ops_AttrList))
	{
		data = INST_DATA(cl,obj);
		data->song = song;
		data->stat = stat;
		data->but = but;
		data->lvl = lvl;
		data->sz = sz;

		set(obj,MUIA_Window_ID,MAKE_ID('D','O','W','N'));
		set(obj,MUIA_Window_Title,MSG_DL_TITLE);

		DoMethod(but,MUIM_Notify,MUIA_Pressed,FALSE,obj,1,DL_ABORT);
		DoMethod(obj,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,obj,1,DL_CLOSE);

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


void dl_addq(songdata sd)
{
	dldata song;
	Object *win;

	song = malloc(sizeof(_dldata));
	if(!song) return;
	memset(song,0,sizeof(_dldata));

	song->sock = -1;
	strcpy(song->title,sd->title);
	strcpy(song->user,sd->user);
	song->size = sd->size;
	sprintf(song->info,MSG_DL_INFORMATION,
			   sd->title,sd->user,nap_getlinktype(sd->link),sd->size,sd->bitrate,sd->freq);

	win = NewObject(gui->dl_mcc->mcc_Class,NULL,DL_SONG,(u_long)song,TAG_DONE);
	if(win==0) {
		free(song);
		return;
	}
	song->window=win;
	DoMethod(gui->app,OM_ADDMEMBER,win);
	set(win,MUIA_Window_Open,TRUE);

	if(dl_list) song->next=dl_list;
	dl_list=song;
}


void dl_startq(char *title, char *user, u_long ip, int port)
{
	struct sockaddr_in sin;
	long tmp=1;
	dldata song;
	long s;

	song = dl_find(title,user);
	if(!song) return;
	if(song->state!=0) return;

	DoMethod(song->window,DL_STAT,MSG_DL_CONNECTING);

	s = socket(AF_INET,SOCK_STREAM,0);
	if(s<0) {
		DoMethod(song->window,DL_STAT,MSG_ERR_SOCKETERROR);
		return;
	}
	sin.sin_addr.s_addr= ip;
gui_debugf("+++host ip %d.%d.%d.%d\n",(ip&0xFF000000)>>24,(ip&0xFF0000)>>16,(ip&0xFF00)>>8,ip&0xFF);
	sin.sin_port=port;
	sin.sin_family=AF_INET;
	sin.sin_len=sizeof(sin);
	IoctlSocket(s,FIOASYNC,(char *)&tmp);
	IoctlSocket(s,FIONBIO,(char *)&tmp);
	song->state=1;
	song->sock=s;
	song->ip = ip;
	tmp = connect(s,(struct sockaddr *)&sin,sin.sin_len);
	if(tmp != -1) return;
	if (Errno()==EINPROGRESS) return;
	_dl_abort(song,(char *)MSG_ERR_CONNECTERROR);
}


dldata dl_find(char *title, char *user)
{
	dldata song;

	song = dl_list;
	while(song) {
		if(strcmp(song->user,user)==0 && strcmp(song->title,title)==0) return(song);
		song = song->next;
	}
	return(NULL);
}


void dl_pollall(void)
{
	dldata song;
	song = dl_list;
	while(song) {
		if(song->state!=0) _dl_pollq(song);
		song = song->next;
	}
}


int dl_flush(void)
{
	dldata song=dl_list,old=dl_list,next;
	while(song) {
		next = song->next;
		if(song->abort==1) {
			set(song->window,MUIA_Window_Open,FALSE);
			DoMethod(gui->app,OM_REMMEMBER,song->window);
			MUI_DisposeObject(song->window);
			if(song==dl_list) {
				dl_list = next;
			} else {
				old->next = next;
			}
			free(song);
		} else {
			old = song;
		}
		song = next;
	}
	if(dl_list==NULL) return(1);
	return(0);
}


void _dl_pollq(dldata song)
{
	static unsigned char buf[4096];
	struct fd_set fds;
	struct timeval tv;
	long s = song->sock;

	FD_ZERO(&fds);
	FD_SET(song->sock,&fds);
	tv.tv_sec=0;
	tv.tv_usec=15;

	if(song->state==1) {
		if (WaitSelect(s+1,NULL,&fds,NULL,&tv,0)==1) {
			long tmp=0;
			song->state=2;
			IoctlSocket(s,FIONBIO,(char *)&tmp);
			return;
		}

	} else if(song->state==2) {
		if(WaitSelect(s+1,&fds,NULL,NULL,&tv,0)==1) {
			int r;
			r = recv(s,buf,1,0);
			if(r!=1 || (r==1 && buf[0]!='1')) {
				_dl_abort(song,(char *)MSG_DL_LOSTCONNECTION);
				return;
			}
			DoMethod(song->window,DL_STAT,MSG_DL_REQFILE);
			song->state=3;
			sprintf(buf,"GET");
			send(s,buf,3,0);
			sprintf(buf,"%s \"%s\" 0",pref_user,song->title);
			send(s,buf,strlen(buf),0);
			return;
		}
	} else if(song->state==3) {
		if(WaitSelect(s+1,&fds,NULL,NULL,&tv,0)==1) {
			int i;
			for(i=0;i<strlen(buf);i++) {
				if(1!=recv(s,&buf[i],1,0)) {
					_dl_abort(song,(char *)MSG_DL_LOSTCONNECTION);
					return;
				}
				if(buf[i]==255) break;
			}
			buf[i]=0;
gui_debugf("+++ header says %s\n",buf);

			DoMethod(song->window,DL_STAT,MSG_DL_DOWNLOADING);
			song->state=4;
			DoMethod(song->window,DL_GAUGE,song->size);
			_dl_filename(buf,song->title);
gui_debugf("+++ file: %s\n",buf);
			song->f = Open(buf,MODE_READWRITE);
			if(!song->f) {
				_dl_abort(song,(char *)MSG_DL_OPENERROR);
				return;
			}
			buf[0]=255;
			FWrite(song->f,buf,1,1);
			song->cur=1;
			return;
		}

	} else if(song->state==4) {
		while(WaitSelect(s+1,&fds,NULL,NULL,&tv,0)==1) {
			int len;
			len=recv(s,buf,4090,0);
			if(len<1) {
				if(len==0 && ((song->cur) >= (song->size))) {
					_dl_abort(song,(char *)MSG_DL_DOWNLOADFINISHED);
					return;
				}
				if (len==0) {
					_dl_abort(song,(char *)MSG_DL_LOSTCONNECTION);
					return;
				}
				_dl_abort(song,(char *)MSG_ERR_NETWORKERROR);
				return;
			}
			FWrite(song->f,buf,1,len);
			song->cur = song->cur + len;
			DoMethod(song->window,DL_UPDATE,song->cur);
			FD_ZERO(&fds);
			FD_SET(song->sock,&fds);
			tv.tv_sec=0;
			tv.tv_usec=15;
		}

	}
}


void _dl_abort(dldata song, char *msg)
{
/*	static char name[1024];
	static char comment[1024];
	struct hostent *he_ip,*he_host;
*/
	if(song->sock!= -1) CloseSocket(song->sock);
	song->sock = -1;
	if(song->f != 0) {
		Close(song->f);

/*		he_ip = gethostbyname(Inet_NtoA(song->ip));
		he_host = gethostbyaddr(he_ip->h_addr_list[0],he_ip->h_length,AF_INET);
		if(he_host) sprintf(comment,"from %s (@%s)",song->user,he_host->h_name);
		else sprintf(comment,"from %s (@%s)",song->user,Inet_NtoA(song->ip));

		if(song->state<5) sprintf(comment,"%s; size:%ld",comment,song->size);

		_dl_filename(name,song->title);
		SetComment(name,comment);
*/	}
	song->f = 0;
	song->state=0;
	DoMethod(song->window,DL_STAT,msg);
}


void _dl_filename(char *buf, char *title)
{
	char tmp;
	int i;

	for(i=strlen(title);i>0;i--) {
		tmp = title[i-1];
		if(tmp==':' || tmp=='/' || tmp=='\\') break;
	}

	if(!pref_dlpath)
		sprintf(buf,"PROGDIR:%s",title+i);
	else if (pref_dlpath[strlen(pref_dlpath)-1]==':')
		sprintf(buf,"%s%s",pref_dlpath,title+i);
	else if (pref_dlpath[strlen(pref_dlpath)-1]=='/')
		sprintf(buf,"%s%s",pref_dlpath,title+i);
	else
		sprintf(buf,"%s/%s",pref_dlpath,title+i);
}
