#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include "kernel.h"
#include "swis.h"

#define Buggy "<Obey$Dir>.Buggy"
#include "Buggy.h" // my debugging code
#include "Wimpy.h" // some Wimp definitions

_kernel_swi_regs  SWIRegs;
#define SWI(swi)  SWIcall(swi,True,#swi,__FILE__,__LINE__)
#define SWIX(swi) SWIcall(swi,False,#swi,__FILE__,__LINE__)
#define SWIE(swi) SWIcall(-1,True,#swi,__FILE__,__LINE__)

// dummy since I am on commandline mode
void WimpCloseDown(void) { }

// Call an SWI and abort in case of error...
// Call with SWI NULL to just abort
_kernel_oserror *SWIcall(int SWIName, int AbortOnError, char *AbortMessage, char *File, int Line) {
	_kernel_oserror *OSError;

	//BuggyRegs(AbortMessage,4);

	// Call the SWI (if I have one)
	if (SWIName != -1) {
		OSError = _kernel_swi(SWIName,&SWIRegs,&SWIRegs);
		if (OSError == NULL) { return(OSError); }
	}

	// In case of error write debug log and optionally abort
	BuggyError(AbortMessage,File,Line);
	if (AbortOnError) {
		SWIRegs.r[0] = (int)OSError;
		SWIRegs.r[1] = 1;
		SWIRegs.r[2] = (int)AbortMessage;
		_kernel_swi(Wimp_ReportError,&SWIRegs,&SWIRegs);
		WimpCloseDown();
	}

	return(OSError);
}

typedef struct {
	int Objecttype, Filetype;
	unsigned Length;
	t_OSTimestamp Timestamp;
//	t_DateTimeOrdinalBlock DateTime;
//	int Age;
	char *Name;
	char *Path;
	int Level;
} t_Directory_Entry;


// I need to do it this way so that I can have a pointer to itself inside
typedef struct t_Directory_List t_Directory_List;

struct t_Directory_List {
	t_Directory_List *Prev,*Next;
	int Objecttype, Filetype;
	t_OSTimestamp Timestamp;
	char *Name;
	char *Path;
	char FullPath[];
};

char              MyPath[4096];
t_Directory_List  *MyList = NULL;


void Bla(char *Lab,char *Tim, int n) {
	char *t = Tim;
	int i;
	printf("%s: ",Lab);
	for (i=0;i<n;i++) { printf("%02X ",*t++); }
	printf("\n");
}


void ShowDateTime(char *Bla,t_OSTimestamp *Timestamp) {

	t_DateTimeOrdinalBlock DateTime;
	int i;

	printf("\n%s: ",Bla);
	for (i=0;i<5;i++) { printf("%02X ",Timestamp->All[i]); }
	printf("\n");

	SWIRegs.r[0] = -1;
	SWIRegs.r[1] = (int)(Timestamp);
	SWIRegs.r[2] = (int)(&DateTime);
	SWIX(Territory_ConvertTimeToOrdinals);

	printf("TT.MM.YY HH:MM:SS %02d.%02d.%04d %02d:%02d:%02d\n",DateTime.Day,DateTime.Month,DateTime.Year,DateTime.Hour,DateTime.Minute,DateTime.Second);
}


t_Centiseconds Centiseconds(t_OSTimestamp *OSTimestamp) {
	t_Centiseconds c = 0;
	int i;

	for (i=4;i>=0;i--) {
		c = (c<<8) + (t_Centiseconds)(OSTimestamp->All[i]);
	}
	return(c);
}

int Time_AgeDays(t_OSTimestamp *OSTimestamp) {

	t_Centiseconds f,n;
	t_OSTimestamp  Now;

	SWIRegs.r[0] = 14;
	Now.All[0] = 3;
	SWIRegs.r[1] = (int)(&Now);
	SWIX(OS_Word);
	n = Centiseconds(&Now);

	f = Centiseconds(OSTimestamp);
	return ((int)((n-f) / (100 * 60 * 60 * 24)));
}

int String_Compare(char *s, char *t) {
	int d;
	for (;; s++, t++) {
		d = tolower(*s) - tolower(*t);
		if ((d != 0) || (*s == 0) || (*t == 0)) { return d; }
	}
}

int String_MatchStar(char *s, char *p) {
	int d;
	if (p == NULL) { return 0; }
	for (;; s++, p++) {
		if (*p == '*') { return 0; }
		d = tolower(*s) - tolower(*p);
		if ((d != 0) || (*s == 0) || (*p == 0)) { return d; }
	}
}


typedef void t_Directory_NextFileFunc(t_Directory_Entry *Entry);


int Directory_ScanSub(char *Path, int Level, t_Directory_Entry* Entry, t_OSGBPB10Block *Block, char *Wildcard, int Filetype, int MinAge, int MaxAge, int FilterType, int FilterDown, int MaxDepth, t_Directory_NextFileFunc Func) {

	int Next = 0;
	char *c;
	int age;

	// Loop through the whole thing
	while (Next != -1) {

		// get next entry...
		SWIRegs.r[0] = 10;
		SWIRegs.r[1] = (int)(Path);
		SWIRegs.r[2] = (int)(Block);
		SWIRegs.r[3] = 1;
		SWIRegs.r[4] = Next;
		SWIRegs.r[5] = sizeof(t_OSGBPB10Block);
		SWIRegs.r[6] = 0;
		if (SWIX(OS_GBPB) != NULL) { Next = -1; return(False); }
		Next = SWIRegs.r[4];

		if (SWIRegs.r[3] == 0) { continue; }

		// Simple things
		Entry->Path = Path;
		Entry->Level = Level;
		Entry->Length = Block->Length;
		Entry->Name = (char*)&(Block->Name);

		// Enhance type of object
		Entry->Objecttype = Block->Objecttype;
		// Image flag
		if (Entry->Objecttype == (FilerMask_TypeFolder | FilerMask_TypeFile)) {
			Entry->Objecttype =  Entry->Objecttype | FilerMask_TypeImage;
		}
		// Application flag
		if ((Entry->Objecttype & FilerMask_TypeFolder) == FilerMask_TypeFolder) {
			if (Entry->Name[0] == '!') {
				Entry->Objecttype = (Entry->Objecttype & ~FilerMask_TypeFolder) | FilerMask_TypeApplication;
			}
		}

		// LoadExec to Filetype and Timestamp
		if ((Block->Load & LoadExecMask) == LoadExecMask) {
			Entry->Filetype = (Block->Load >> 8) & 0xFFF;
			Entry->Timestamp.Parts.High = (char)(Block->Load & 0xff);
			Entry->Timestamp.Parts.Low  = Block->Exec;
			}
		else {
			Entry->Filetype = -1;
			Entry->Timestamp.Parts.High = 0;
			Entry->Timestamp.Parts.Low  = 0;
			}

		// Get age
// no	Entry->Age = Time_AgeDays(&(Entry->Timestamp));

		// Format timestamp
// no	SWIRegs.r[0] = -1;
// no	SWIRegs.r[1] = (int)((char*)(&(Entry->Timestamp)));
// no	SWIRegs.r[2] = (int)(&(Entry->DateTime));
// no	SWIX(Territory_ConvertTimeToOrdinals);

		// Check if we want to deliver this one...
		do {

			// Name I cope with x* only
			if (String_MatchStar(Entry->Name,Wildcard) != 0) { continue; }

			// Check Filetype - if no good or not a file get next entry
			if (Filetype >= 0) {
				if ((Entry->Filetype != Filetype) || (Entry->Objecttype & FilerMask_TypeFile) == 0) { continue; }
			}

			// Check Objecttype
			if ((Entry->Objecttype & FilterType) != 0) { continue; }

			// Check age
			if ((MinAge != -1) || (MaxAge != 0)) {
				age = Time_AgeDays(&(Entry->Timestamp));
				if ((MinAge != -1) && (age < MinAge)) { continue; }
				if ((MaxAge != -1) && (age > MaxAge)) { continue; }
			}

			// I got one... that I care about... so tell about it
			Func(Entry);
		} while(False);

		// Question arising is if I first delve deeper or not...
		if ((MaxDepth >= 0) && (Level > MaxDepth)) { continue; }
		if ((Entry->Objecttype & FilterDown) != 0) { continue; }

		// We delve deeper?
		if ((Entry->Objecttype == FilerMask_TypeFolder) || (Entry->Objecttype == FilerMask_TypeApplication)) {
			strcat(Path,".");
			strcat(Path,Entry->Name);
			// Off we dive
			Directory_ScanSub(Path, Level+1, Entry, Block, Wildcard, Filetype, MinAge, MaxAge, FilterType, FilterDown, MaxDepth, Func);
			// Come back up
			c = strrchr(Path,'.');
			*c = 0;
		}

	}
	return(True);
}



#define Directory_ScanFull(Path,Func) Directory_Scan(Path, NULL,-1,-1,-1,0,0,-1,Func)
#define Directory_ScanFlat(Path,Func) Directory_Scan(Path, NULL,-1,-1,-1,0,0,0,Func)

int Directory_Scan(char *Path, char *Wildcard, int Filetype, int MinAge, int MaxAge, int FilterType, int FilterDown, int MaxDepth, t_Directory_NextFileFunc Func) {
	t_Directory_Entry Entry;
	t_OSGBPB10Block Block;
	return(Directory_ScanSub(Path,0,&Entry,&Block,Wildcard,Filetype,MinAge,MaxAge,FilterType,FilterDown,MaxDepth,Func));
}


void AddToMyList(t_Directory_Entry *Entry) {

	int i;
	char *SubPath;

	// Allocate next list entry
	t_Directory_List *e = malloc(sizeof(t_Directory_List) + strlen(Entry->Path) + 1 + strlen(Entry->Name) + 1);
	if(e == NULL) { SWIE("AddToMyList malloc"); }

	// Put that entry at the top of the list
	if (MyList != NULL) { MyList->Prev = e; }
	e->Prev = NULL;
	e->Next = MyList;
	MyList = e;

	// Fill that entry
	e->Objecttype = Entry->Objecttype;
	e->Filetype = Entry->Filetype;
	memcpy(&(e->Timestamp),&(Entry->Timestamp),sizeof(t_OSTimestamp));
	strcpy(e->FullPath,Entry->Path);
	strcat(e->FullPath,".");
	strcat(e->FullPath,Entry->Name);

	// get pointer to the relative path
	i = Entry->Level;
	SubPath = (char*)(&(e->FullPath));
	SubPath += strlen(SubPath);
	while (i >= 0) {
		do { SubPath --; } while (*SubPath != '.');
		i--;
	}

	e->Path = SubPath +1;
	e->Name = strrchr(SubPath,'.') +1;

}

void PrintMyList(void) {
	t_Directory_List *l = MyList;
	int i=0;

	printf("   AAAA PREV NEXT TTT FFF .................... - Path\n");
	while (l != NULL) {
		printf("%02d %04x %04x %04x %03X %03X %20s - %s\n",i,(int)l,(int)(l->Prev),(int)(l->Next),l->Objecttype,l->Filetype,l->Name,l->Path);
		l = l->Next;
		i++;
	}
}

#define PrintOne(t,l) printf("%s %04x %04x %04x %03X %03X %20s - %s\n",t,(int)l,(int)(l->Prev),(int)(l->Next),l->Objecttype,l->Filetype,l->Name,l->Path)

void SortMyList(char SortFeld) {

	// My sorted list is compact - so swapping data will NOT work but most sorts need just that!
	// So I look for the smalles one and put that at the start of the list and then move to next entry and repeat

	t_Directory_List *strt = MyList;
	t_Directory_List *mini,*look;
	int i;

	// start at beginning and go on one by one
	while (strt != NULL) {
//printf("\n-------------------\n");
//PrintOne("strt go",strt);
		// look in rest of list for smallest
		mini = strt;
		look = strt; //->Next;

		while ((look = look->Next) != NULL) {
			//if (String_Compare(mini->Path,look->Path) > 0) { mini = look; }
			switch (SortFeld) {
			case 'N':
				i = String_Compare(mini->Name,look->Name);
				if (i > 0) { mini = look; }
				if (i == 0) {
					if (String_Compare(mini->Path,look->Path) > 0) { mini = look; }
				}
				break;
			case 'P':
				if (String_Compare(mini->Path,look->Path) > 0) { mini = look; }
				break;
			case 'F':
				if (String_Compare((char*)(&mini->FullPath),(char*)(&look->FullPath)) > 0) { mini = look; }
				break;
			}
			//look = look->Next;
		}

//PrintOne("mini go",mini);
		// now I have the smallest one so we swap if needed
		if (mini != strt) {
			// remove mini from list
			if (mini->Next == NULL) {
				(mini->Prev)->Next = NULL;
			}
			else {
				(mini->Prev)->Next = mini->Next;
				(mini->Next)->Prev = mini->Prev;
			}
			// insert mini bevor strt
			if (strt->Prev == NULL) {
				MyList = mini;
				mini->Prev = NULL;
				mini->Next = strt;
				strt->Prev = mini;
			}
			else {
				(strt->Prev)->Next = mini;
				mini->Prev = strt->Prev;
				strt->Prev = mini;
				mini->Next = strt;
			}
//PrintMyList();
			strt = mini;
		}

		// go for rest of list
		strt = strt->Next;
	}

}




	int main (int argc, char *argv[]) {

		if (argc != 2) {
			printf("'nen Pfad braucht's schon als Parameter! \n");
			return 4;
		}

		// get my path
		strcpy(MyPath,argv[1]);

		Directory_Scan((char*)&MyPath,"!Sprites*",0xff9,-1,-1,0,0,1,AddToMyList);

		printf("\nOriginal\n"); PrintMyList();

		SortMyList('P');

		printf("\nPath\n"); PrintMyList();

		SortMyList('N');

		printf("\nName\n"); PrintMyList();

		return 0;
	}
