#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;


char              MyPath[4096];

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_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 == (char)0) || (*p == (char)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;

	// 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
		Entry->Age = Time_AgeDays(&(Entry->Timestamp));

		// Format timestamp
		SWIRegs.r[0] = -1;
		SWIRegs.r[1] = (int)((char*)(&(Entry->Timestamp)));
		SWIRegs.r[2] = (int)(&(Entry->DateTime));
		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) && (Entry->Age < MinAge)) { continue; }
			if ((MaxAge != -1) && (Entry->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 MyNextFile(t_Directory_Entry *Entry) {
	char typ[8];
	if ((Entry->Objecttype & FilerMask_TypeFile)        != 0) { strcpy(typ,"fil"); }
	if ((Entry->Objecttype & FilerMask_TypeApplication) != 0) { strcpy(typ,"app");  }
	if ((Entry->Objecttype & FilerMask_TypeFolder)      != 0) { strcpy(typ,"dir");  }
	if ((Entry->Objecttype & FilerMask_TypeImage)       != 0) { strcat(typ,"!");    }
	else                                                      { strcat(typ," ");    }
	printf("%s %03X %03X %04d %20s - %s\n",typ,Entry->Objecttype,Entry->Filetype,Entry->Age,Entry->Name,Entry->Path);
}


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]);

	printf("     TTT FFF AAAA .................... - Path\n");
	Directory_Scan((char*)&MyPath,"!*",0xff9,-1,-1,0,0,1,MyNextFile);


	return 0;
}
