/* MED - music editor © 1989, 1990 by Teijo Kinnunen */
/* med-pathlist.c: pathlist handling.. */
#include <ctype.h>
#include <dos.h>
#include "med.h"
#include "medproto.h"
#define DIRSIZE (sizeof(struct PSDir))
#define NAMESIZE (sizeof(struct PSName))

extern struct Task *maintsk;
extern struct Window *window;
extern struct Kappale far song;
extern struct Soitin *sample[];
extern UBYTE samplenum,hakemisto[];
extern UWORD nykyinenosio,chip mouse0[],chip mouse1[];
extern struct RastPort *wrp;
extern struct FData *firstfd;
extern BPTR fh;
static struct PSDir *firstdir = NULL,*lastdir = NULL,*dispdir = NULL;
static struct PSName *dispname = NULL;
static UBYTE *spaces = "                ",cdirn = 0,dirs = 0;
static UWORD centr = 0;
UWORD lastrep,lastreplen;
UBYTE lastmidic,lastmidip;

static UWORD __regargs Getposnum(struct PSName *,struct PSName *);
static struct PSDir * __regargs MakeNewDir(char *);
static struct PSName * __regargs MakeNewEntry(char *);
static void __regargs InsertSampleName(struct PSName *,struct PSDir *);
static BOOL __regargs DirAvailable(char *);
static void __regargs FindAvailDirs(void);
static void __regargs RemoveDir(struct PSDir *);
static void __regargs RemoveName(void);
static void __regargs AddName(void);
static void __regargs AddDir(void);
static void __regargs GetRepVals(struct PSName *);
static void __regargs StripRepNums(char *,char *);
static char * __regargs SavePathFile(char *);
static void __regargs DispEntry(UWORD);
static BOOL __regargs SearchFromDir(struct PSDir *,char *);
static struct PSDir * __regargs SearchDirNum(UBYTE);
static BOOL __regargs ConstrName(char *,char *,BOOL,struct PSDir **);

static UWORD __regargs Getposnum(struct PSName *ptr,struct PSName *this)
{
	register UWORD pos = 0;
	if(!ptr || !this) return(0);
	while(ptr && ptr != this) { pos++; ptr = ptr->next; }
	return(pos);
}

static struct PSDir * __regargs MakeNewDir(char *name)
{
	UWORD dirlen = DIRSIZE + strlen(name) + 1;
	struct PSDir *newdir = malloc(dirlen);
	if(!newdir) return(0L);
	newdir->namelen = strlen(name);
	newdir->entries = newdir->diravail = 0;
	strcpy((char *)newdir+DIRSIZE,name);
	newdir->prev = newdir->next = 0L;
	newdir->first = newdir->last = 0L;
	dirs++;
	return(newdir);
}

static struct PSName * __regargs MakeNewEntry(char *name)
{
	UWORD entrylen = NAMESIZE + strlen(name) + 1;
	struct PSName *newentry = malloc(entrylen);
	if(!newentry) return(0L);
	newentry->namelen = strlen(name);
	newentry->firstletter = toupper(*name);
	strcpy((char *)newentry+NAMESIZE,name);
	return(newentry);
}

static void __regargs InsertSampleName(struct PSName *entr,struct PSDir *dir)
{
	register struct PSName *first = dir->first;
	dir->entries++;
	if(!first) {
		dir->first = dir->last = entr;
		entr->next = entr->prev = 0L;
		return;
	}
	for(;stricmp((char *)first + NAMESIZE,
	    (char *)entr + NAMESIZE) <= 0 && first->next;
	    first = first->next);
	if(!first->next && stricmp((char *)first + NAMESIZE,
		(char *)entr + NAMESIZE) < 0) {
		entr->next = 0L;
		first->next = entr;
		entr->prev = first;
		dir->last = entr;
		return;
	}
	entr->next = first;
	if(entr->prev = first->prev) entr->prev->next = entr;
	else dir->first = entr;
	first->prev = entr;
}

static BOOL __regargs DirAvailable(char *name)
{
	BPTR lock;
	char tstname[120];
	APTR prevwptr;
	struct Process *tpr = (struct Process *)maintsk;
	prevwptr = tpr->pr_WindowPtr;
	tpr->pr_WindowPtr = (APTR)-1;
	strcpy(tstname,name);
	if(tstname[strlen(name) - 1] == '/') tstname[strlen(name) - 1] = 0;
	lock = Lock(tstname,ACCESS_READ);
	tpr->pr_WindowPtr = prevwptr;
	if(lock) {
		UnLock(lock);
		return(TRUE);
	}
	return(FALSE);
}

static void __regargs FindAvailDirs()
{
	struct PSDir *dir = firstdir,*dir2 = dir;
	char thisdskname[FMSIZE],cmpdskname[FMSIZE];
	while(dir2) { dir2->diravail = 2; /* 2=don't know */ dir2 = dir2->next; }
	while(dir) {
		if(dir->diravail == 2) {
			if(DirAvailable((char *)dir + DIRSIZE)) {
				dir->diravail = 1;
				strsfn((char *)dir + DIRSIZE,thisdskname,
					NULL,NULL,NULL);
				dir2 = dir->next;
				while(dir2) {
					strsfn((char *)dir2 + DIRSIZE,
						cmpdskname,NULL,NULL,NULL);
					if(!strcmp(thisdskname,cmpdskname))
						dir2->diravail = 1;
					dir2 = dir2->next; /* ^disk is available */
				}
			} else dir->diravail = 0;
		}
		dir = dir->next;
	}
}

void LoadPathFile()
{
	UBYTE *scanptr,*pathlist;
	ULONG pathlistlen;
	BPTR fh;
	struct PSDir *currentdir;
	struct PSName *currname;
	fh = Open("MED_paths",MODE_OLDFILE);
	if(!fh) fh = Open("S:MED_paths",MODE_OLDFILE);
	if(!fh) {
		Ilmoita("MED_paths not found.");
		return; /* ei löytynyt */
	}
	Seek(fh,0,OFFSET_END);
	pathlistlen = Seek(fh,0,OFFSET_BEGINNING);
	if(!(pathlist = malloc(pathlistlen))) {
		Close(fh);
		fh = NULL;
		Ilmoita("No memory for path list.");
		return;
	}
	StartLoad();
	Ilmoita("Loading MED_paths...");
	Read(fh,pathlist,pathlistlen);
	Close(fh);
	fh = NULL;
	Ilmoita("Sorting MED_paths...");
	for(scanptr = pathlist;scanptr < pathlist+pathlistlen; scanptr++)
		if(*scanptr == '\n') *scanptr = '\0';
	/* and now begin to translate the information to structures */
	scanptr = pathlist;
	while(scanptr < pathlist + pathlistlen) {
		if(*scanptr == '*') {
			if(!(currentdir = MakeNewDir(scanptr+1))) goto lpferr;
			if(!firstdir) firstdir = lastdir = currentdir;
			else {
				lastdir->next = currentdir;
				currentdir->prev = lastdir;
				lastdir = currentdir;
			}
			scanptr += strlen(scanptr+1) + 1;
		}
		else if(currentdir) {
			if(!(currname = MakeNewEntry(scanptr))) goto lpferr;
			InsertSampleName(currname,currentdir);
			scanptr += strlen(scanptr);
		}
		scanptr++;
	}
lpferr:	free(pathlist);
	dispdir = firstdir;
	if(firstdir) dispname = firstdir->first;
	StopLoad();
	Ilmoita(NULL);
	return;
}

static void __regargs AddDir()
{
	struct PSDir *newdir;
	struct PSName *newname;
	struct FData *scanptr;
	char newdirname[FMSIZE],fc;
	strcpy(newdirname,hakemisto);
	if(!(fc = strlen(newdirname))) return;
	fc = newdirname[fc - 1];
	if(fc != ':') strcat(newdirname,"/");
	if(!(newdir = MakeNewDir(newdirname))) return;
	newdir->next = dispdir;
	if(dispdir) {
		if(dispdir->prev) {
			newdir->prev = dispdir->prev;
			dispdir->prev->next = newdir;
		}
		dispdir->prev = newdir;
	}
	if(firstdir == dispdir) firstdir = newdir;
	dispdir = newdir;
	dirs++;
	for(scanptr = firstfd; scanptr; scanptr = scanptr->next) {
		if(scanptr->type > 0) continue;
		if(!(newname = MakeNewEntry(scanptr->fname))) break;
		InsertSampleName(newname,newdir);
	}
	cdirn = Getposnum((struct PSName *)firstdir,(struct PSName *)dispdir);
	dispname = dispdir ? dispdir->first : 0L;
	centr = 0;
	DisplayPathList();
 	DisplayDirName();
}

static char * __regargs SavePathFile(char *name)
{
	struct PSDir *wrdir = firstdir;
	struct PSName *wrname;
	char *wptr,wsiz;
	if(!firstdir) return("No path list!!");
	if(!(fh = Open2(name,MODE_NEWFILE))) return(DISKERR);
	Ilmoita("Saving path list...");
	while(wrdir) {
		wptr = (char *)wrdir + DIRSIZE;
		wsiz = strlen(wptr);
		if(Write(fh,"*",1) != 1) return(DISKERR);
		if(Write(fh,wptr,wsiz) != wsiz) return(DISKERR);
		if(Write(fh,"\n",1) != 1) return(DISKERR);
		wrname = wrdir->last;
		while(wrname) { /* write in Z - A order: it's faster to load */
			wptr = (char *)wrname + NAMESIZE;
			wsiz = strlen(wptr);
			if(Write(fh,wptr,wsiz) != wsiz) return(DISKERR);
			if(Write(fh,"\n",1) != 1) return(DISKERR);
			wrname = wrname->prev;
		}
		wrdir = wrdir->next;
	}
	if(fh) { Close(fh); fh = 0L; }
	Ilmoita("Saved.");
	return(NOERR);
}

static void __regargs RemoveDir(struct PSDir *dir)
{
	struct PSName *delname,*del2;
	if(!dir) return;
	delname = dir->first;
	if(dir->next) dir->next->prev = dir->prev;
	if(dir->prev) dir->prev->next = dir->next;
	if(firstdir == dir) firstdir = dir->next;
	dirs--;
	if(dispdir == dir)
		if(dispdir = (dir->next ? dir->next : dir->prev))
			dispname = dispdir->first;
		else	dispname = 0L;
	while(delname) {
		del2 = delname->next;
		free((char *)delname);
		delname = del2;
	}
	free((char *)dir);
	cdirn = Getposnum((struct PSName *)firstdir,(struct PSName *)dispdir);
}

void FreePaths()
{
	struct PSDir *deldir = firstdir,*ndeld;
	Ilmoita("Freeing path list...");
	while(deldir) {
		ndeld = deldir->next;
		RemoveDir(deldir);
		deldir = ndeld;
	}
}

static void __regargs AddName()
{
	char newentry[100],*ptr;
	struct PSName *newname;
	struct SongSample *ss = &song.sample[samplenum];
	if(!dispdir) return;
	ptr = stpcpy(newentry,ss->sname);
	if(ss->midich) {
		*ptr++ = ':'; *ptr++ = ':'; *ptr++ = 'M';
		ptr += stcu_d(ptr,ss->midich);
		*ptr++ = '/';
		ptr += stcu_d(ptr,ss->midipreset);
	} else if(ss->rep || ss->replen > 1) {
		*ptr++ = ':'; *ptr++ = ':';
		ptr += stcu_d(ptr,ss->rep << 1);
		*ptr++ = '/';
		stcu_d(ptr,ss->replen << 1);
	}
	if(!(newname = MakeNewEntry(newentry))) return;
	InsertSampleName(newname,dispdir);
	dispname = newname;
	DisplayPathList();
}

static void __regargs RemoveName()
{
	struct PSDir *remdir = dispdir;
	struct PSName *remptr = dispname;
	if(!remptr || !remdir) return;
	if(remptr->prev) remptr->prev->next = remptr->next;
	if(remptr->next) {
		remptr->next->prev = remptr->prev;
		dispname = remptr->next;
	} else dispname = remptr->prev;
	remdir->entries--;
	if(remdir->first == remptr) remdir->first = remptr->next;
	if(remdir->last == remptr) remdir->last = remptr->prev;
	free((char *)remptr);
	centr = Getposnum(remdir->first,dispname);
}

void DisplayDirName()
{
	UBYTE txt[2] = { '0','0' },*dspdirn,*dspdirs,ddnl;
	if(nykyinenosio != 0xB) return;
	SetAPen(wrp,1);
	SetBPen(wrp,3);
	Move(wrp,147,26);
	if(!dispdir) {
		Text(wrp,"----------",10);
		txt[0] = txt[1] = '-';
	} else {
		dspdirs = dspdirn = (char *)dispdir + DIRSIZE;
		dspdirn += strlen(dspdirn) - 2;
		while(dspdirn >= dspdirs && *dspdirn != '/' && *dspdirn != ':')
			dspdirn--;
		dspdirn++;
		if((ddnl = strlen(dspdirn)) >= 10)
			Text(wrp,dspdirn,10);
		else {
			Text(wrp,dspdirn,ddnl);
			Text(wrp,spaces,10 - ddnl);
		}
	}
	Move(wrp,211,37);
	txt[0] += cdirn / 10;
	txt[1] += cdirn % 10;
	Text(wrp,txt,2);
}

void DisplayPathList()
{
	struct PSName *dptr;
	UBYTE dispcnt;
	if(nykyinenosio != 0xB) return;
	SetAPen(wrp,0);
	SetBPen(wrp,3);
	wrp->Mask = 0x3;
	if(!dispname || !dispdir) for(dispcnt = 0; dispcnt < 5; dispcnt++) {
		Move(wrp,7,26 + 8 * dispcnt);
		Text(wrp,spaces,16);
	} else for(dispcnt = 0; dispcnt < 5; dispcnt++) {
		Move(wrp,7,26 + 8 * dispcnt);
		switch(dispcnt) {
			case 0:	if(dptr = dispname->prev) dptr = dptr->prev;
				break;
			case 1:	dptr = dispname->prev; break;
			case 2:	dptr = dispname; SetBPen(wrp,1); break;
			case 3: dptr = dispname->next; SetBPen(wrp,3); break;
			case 4: if(dptr = dispname->next) dptr = dptr->next;
		}
		if(!dptr) Text(wrp,spaces,16);
		else {
			if(dptr->namelen >= 16)	Text(wrp,(char *)dptr +
				NAMESIZE,16);
			else {
				Text(wrp,(char *)dptr + sizeof(struct
					PSName),dptr->namelen);
				Text(wrp,spaces,16 - dptr->namelen);
			}
			dptr = dptr->next;
		}
	}
	wrp->Mask = 0xff;
}

static void __regargs GetRepVals(struct PSName *name)
{
	UBYTE *tstchr,midi = 0;
	ULONG rvals[2] = { 0,0 },*numptr = rvals;
	if(!name) return;
	lastrep = lastreplen = 0;
	lastmidic = lastmidip = 0;
	tstchr = (UBYTE *)name + NAMESIZE;
	while(*tstchr && *tstchr != ':') tstchr++;
	if(!(*tstchr) || *(tstchr+1) != ':') return;
	tstchr += 2;
	if(*tstchr == 'm' || *tstchr == 'M') { midi = 1; tstchr++; }
	while(*tstchr) {
		if(*tstchr == '/') numptr = &rvals[1]; /* now coming length */
		if(isdigit(*tstchr)) {
			*numptr *= 10;
			*numptr += ((*tstchr) - '0');
		}
		tstchr++;
	}
	if(midi) { /* these numbers are not repeat/length, instead they */
		 /* are now midi channel and preset number */
		if(lastrep <= 16) lastmidic = (UBYTE)rvals[0];
		if(lastreplen <= 128) lastmidip = (UBYTE)rvals[1];
	} else {
		lastrep = (UWORD)(rvals[0] >> 1);
		lastreplen = (UWORD)(rvals[1] >> 1);
	}
}

static void __regargs StripRepNums(char *name,char *to)
{
	while(*name && *name != ':') *to++ = *name++;
	*to = 0;
}

void LoadSelectedSample()
{
	char fullname[150],*nameptr;
	if(!dispname || !dispdir) return;
	StartLoad();
	strcpy(fullname,(char *)dispdir + DIRSIZE);
	nameptr = fullname + strlen(fullname);
	StripRepNums((char *)dispname + NAMESIZE,nameptr);
	GetRepVals(dispname);
	strcpy(song.sample[samplenum].sname,nameptr);
	nameptr =  LoadInstrument(fullname,FALSE);
	PaivitaSoittimennimi();
	StopLoad();
	if(nameptr == NOERR) SampleLoaded();
	diskerr(nameptr);
}

static void __regargs DispEntry(UWORD num)
{
	dispname = dispdir->first;
	centr = 0;
	while(num-- && dispname->next) { dispname = dispname->next; centr++; }
	DisplayPathList();
}

void ScrollSLUp(BYTE steps)
{
	if(!dispname) return;
	if(steps < 0) {
		dispname = dispdir->first;
		centr = 0;
	}
	else while(centr && steps--) { dispname = dispname->prev; centr--; }
	DisplayPathList();
}

void ScrollSLDown(BYTE steps)
{
	if(!dispname) return;
	if(steps < 0) {
		dispname = dispdir->last;
		centr = dispdir->entries - 1;
	} else while(centr < (dispdir->entries - 1) && steps--) {
		dispname = dispname->next; centr++;
	}
	DisplayPathList();
}

void HandleB(UWORD gid)
{
	switch(gid) {
		case 0xB00:	ScrollSLUp(1);   break;
		case 0xB02:	ScrollSLDown(1); break;
		case 0xB03: case 0xB04:
			if(!dispdir) break;
			if(gid == 0xB03) { 
				if(dispdir->prev) {
					dispdir = dispdir->prev;
					cdirn--;
				}
			} else {
				if(dispdir->next) {
					dispdir = dispdir->next;
					cdirn++;
				}
			}
			dispname = dispdir->first;
			centr = 0;
			DisplayPathList();
			DisplayDirName();
			break;
		case 0xB01:	ScrollSLUp((BYTE)-1); break;
		case 0xB0C:	ScrollSLDown((BYTE)-1); break;
		case 0xB05:	LoadSelectedSample(); break;
		case 0xB0A:	RemoveName(); DisplayPathList(); break;
		case 0xB0B:	RemoveDir(dispdir);
			 	DisplayPathList(); DisplayDirName(); break;
		case 0xB08:	AddName(); break;
		case 0xB09:	AddDir(); break;
		case 0xB06: case 0xB07:
			StartLoad();
			diskerr(SavePathFile(gid == 0xB06 ? "MED_paths" :
				"S:MED_paths"));
			StopLoad();
	}
}

static BOOL __regargs SearchFromDir(struct PSDir *dir,char *name)
{
	UBYTE matchletter = toupper(*name),name2[110];
	struct PSName *searchptr = (dir ? dir->first : 0L);
	while(searchptr) {
		if(searchptr->firstletter >= matchletter) {
			if(searchptr->firstletter > matchletter) return(FALSE);
			StripRepNums((char *)searchptr+NAMESIZE,name2);
			if(!stricmp(name2,name)) {
				GetRepVals(searchptr);
				return(TRUE);
			}
		}
		searchptr = searchptr->next;
	}
	return(FALSE);
}

static struct PSDir * __regargs SearchDirNum(UBYTE num)
{
	struct PSDir *ptr = firstdir;
	while(ptr && num--) ptr = ptr->next;
	return(ptr);
}

static BOOL __regargs ConstrName(char *fname,char *tname,
	BOOL mustfind,struct PSDir **founddir)
{
	UBYTE dircnt;
	char fpath[FMSIZE];
	struct PSDir *tstdir,*found = 0L;
	*tname = 0;
	*founddir = 0L;
	if(stcgfp(fpath,fname)) {
		if(DirAvailable(fpath) || mustfind) {
			strcpy(tname,fname);
			return(TRUE);
		}
		return(FALSE);
	}
	for(dircnt = 0; dircnt < dirs; dircnt++) {
		tstdir = SearchDirNum(dircnt);
		if(tstdir->diravail && SearchFromDir(tstdir,fname)) {
			found = tstdir;
			break;
		}
	}
	if(mustfind && !found) {
		for(dircnt = 0; dircnt < dirs; dircnt++) {
			tstdir = SearchDirNum(dircnt);
			if(!tstdir->diravail && SearchFromDir(tstdir,fname)) {
				found = tstdir;
				break;
			}
		}
	}
	if(found) strcpy(tname,(char *)found + DIRSIZE);
	*founddir = found;
	strcat(tname,fname);
	return((BOOL)found);
}
	
void ConstructDirName(char *from,char *to)
{
	struct PSDir *dir = firstdir,*here = NULL;
	while(dir) {
		if(SearchFromDir(dir,from)) {
			here = dir;
			if(DirAvailable((char *)dir + DIRSIZE)) break;
		}
		dir = dir->next;
	}
	if(here) strcpy(to,(char *)here + DIRSIZE);
	else *to = '\0';
	strcat(to,from);
}

char *LoadSongSamples() /* the intelligent sample loader */
{
	UBYTE samplelist[63],cnt,stl = 0,fullname[FMSIZE];
	char *res = NOERR;
	struct PSDir *dir;
	FindAvailDirs();
	for(cnt = 0; cnt < 63; cnt++) /* Find number of samples to load */
		if(song.sample[cnt].sname[0] && !song.sample[cnt].midich
			&& !sample[cnt] && stcgfn(fullname,song.sample[cnt].sname)) {
			samplelist[cnt] = 1;
			stl++;
		} else samplelist[cnt] = 0;
	while(stl) {
		for(cnt = 0; cnt < 63; cnt++) {
			if(samplelist[cnt] == 1) {
				if(ConstrName(song.sample[cnt].sname,
/* load everything that can be */	fullname,FALSE,&dir)) {
/* loaded without disk swapping */	samplenum = cnt;
					PaivitaSoittimennimi();
					stl--;
					samplelist[cnt] = 0;
					if(res = LoadInstrument(fullname,FALSE)) {
						diskerr(res);
						if(!Continue()) goto stop;
					}
				}
			}
		}
		for(cnt = 0; cnt < 63; cnt++) {
			if(samplelist[cnt] == 1) {
/* then load a sample which */	ConstrName(song.sample[cnt].sname,
/* needs disk swapping */		fullname,TRUE,&dir);
				samplenum = cnt;
				PaivitaSoittimennimi();
				stl--;
				samplelist[cnt] = 0;
				if(res = LoadInstrument(fullname,FALSE)) {
					diskerr(res);
					if(!Continue()) goto stop;
				}
				if(dir) dir->diravail = 1;
/* try again load without.. */	break;	/* ..swapping */
			}
		}
	}
	return(NOERR);
stop:	return("Loading aborted.");
}
