#include <devices/trackdisk.h>
#include <dos/filehandler.h>
#include <dos/dosextens.h>
#include <exec/memory.h>

#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

extern struct DosLibrary *DOSBase;

struct BitmapBlock
{
	ULONG			Checksum;
	ULONG			Bitmap[127];
};

struct BitmapExtension
{
	ULONG			BitmapPointers[127];
	ULONG			BitmapExtension;
};

struct RootDirectory
{
	ULONG			PrimaryType;
	ULONG			HeaderKey;

	ULONG			WhoKnows0;
	ULONG			HashTableSize;
	ULONG			WhoKnows1;

	ULONG			Checksum;

	ULONG			HashTable[72];

	ULONG			BitmapFlag;

	ULONG			BitmapPointers[25];
	ULONG			BitmapExtension;

	struct DateStamp	LastRootChange;

	UBYTE			DiskName[32];

	ULONG			WhoKnows2[2];

	struct DateStamp	LastDiskChange;
	struct DateStamp	CreationDate;

	ULONG			NextHash;
	ULONG			ParentDir;

	ULONG			WhoKnows3;

	ULONG			SecondaryType;
};

ULONG Bits[32] =
{
	0xFFFFFFFE,0xFFFFFFFD,0xFFFFFFFB,0xFFFFFFF7,
	0xFFFFFFEF,0xFFFFFFDF,0xFFFFFFBF,0xFFFFFF7F,
	0xFFFFFEFF,0xFFFFFDFF,0xFFFFFBFF,0xFFFFF7FF,
	0xFFFFEFFF,0xFFFFDFFF,0xFFFFBFFF,0xFFFF7FFF,
	0xFFFEFFFF,0xFFFDFFFF,0xFFFBFFFF,0xFFF7FFFF,
	0xFFEFFFFF,0xFFDFFFFF,0xFFBFFFFF,0xFF7FFFFF,
	0xFEFFFFFF,0xFDFFFFFF,0xFBFFFFFF,0xF7FFFFFF,
	0xEFFFFFFF,0xDFFFFFFF,0xBFFFFFFF,0x7FFFFFFF
};

UBYTE
Local2Upper(UBYTE c)
{
	return((UBYTE)((((c) >= 224 && (c) <= 254) || ((c) >= 'a' && (c) <= 'z')) ? (c) - 32 : (c)));
}

LONG
Strcmp(UBYTE *a,UBYTE *b)
{
	for( ; Local2Upper(*a) == Local2Upper(*b) ; a++, b++)
	{
		if(!(*a))
			return(0);
	}

	return((LONG)(Local2Upper(*a) - Local2Upper(*b)));
}

ULONG
Checksum(ULONG *DiskSector)
{
	ULONG Sum;
	SHORT i;

	for(Sum = 0, i = 0 ; i < (TD_SECTOR >> 2) ; i++)
		Sum += DiskSector[i];

	return(Sum);
}

LONG
WriteBlock(struct IOExtTD *DevRequest,APTR Buffer,ULONG Offset)
{
	DevRequest -> iotd_Req . io_Command	= CMD_WRITE;
	DevRequest -> iotd_Req . io_Data	= Buffer;
	DevRequest -> iotd_Req . io_Length	= TD_SECTOR;
	DevRequest -> iotd_Req . io_Offset	= Offset * TD_SECTOR;

	return((LONG)DoIO(DevRequest));
}

LONG
EraseDisk(UBYTE *DiskName,struct IOExtTD *DevRequest,ULONG FirstBlock,ULONG Blocks,ULONG Reserved,ULONG DosType)
{
	ULONG			*Bitmap,AllocSize;
	APTR			 Buffer;
	struct BitmapBlock	*BitmapBlock;
	struct BitmapExtension	*BitmapExtension;
	struct RootDirectory	*RootDirectory;
	LONG			 i,Count,Root,Len;
	LONG			 BitmapCount,BitmapBlocks;
	LONG			 ExtensionCount,ExtensionBlocks = 0;
	LONG			 Error;

		/* Number of long words to allocate for the bitmap. */

	AllocSize = (Blocks - Reserved + 31) / 32;

		/* Allocate a block-sized buffer. */

	if(Buffer = AllocMem(TD_SECTOR,MEMF_PUBLIC|MEMF_CHIP))
	{
			/* Clear the buffer. */

		memset(Buffer,0,TD_SECTOR);

			/* Put the DOS type field into the first longword. */

		*(ULONG *)Buffer = DosType;

			/* And write it. */

		if(Error = WriteBlock(DevRequest,Buffer,0 + FirstBlock))
			return(Error);

			/* Clear the other half. */

		memset(Buffer,0,TD_SECTOR);

			/* And write it. */

		if(Error = WriteBlock(DevRequest,Buffer,1 + FirstBlock))
			return(Error);

			/* Allocate space for the bitmap. */

		if(Bitmap = (ULONG *)AllocMem(sizeof(ULONG) * AllocSize,MEMF_PUBLIC))
		{
				/* Mark all blocks as `unused'. */

			for(i = 0 ; i < AllocSize ; i++)
				Bitmap[i] = ~0;

				/* Determine number of bitmap blocks. */

			Count = BitmapBlocks = (AllocSize + 126) / 127;

				/* If there are more than 25 bitmap blocks to create,
				 * we will need at least one bitmap extension block.
				 */

			if(Count > 25)
			{
					/* The old filing system doesn't support bitmap
					 * extension blocks.
					 */

				if(DosType == ID_DOS_DISK)
					return(-1);

					/* Subtract 25 blocks since they will fit into the
					 * root block.
					 */

				Count -= 25;

					/* Add the extension blocks. */

				do
				{
					ExtensionBlocks++;

						/* Subtract the blocks that will fit into the
						 * extension block.
						 */

					if(Count > 127)
						Count -= 127;
					else
						Count = 0;
				}
				while(Count);
			}

				/* Calculate the root block offset. */

			Root = Blocks >> 1;

			RootDirectory = Buffer;

			memset(RootDirectory,0,TD_SECTOR);

				/* Fill in the root directory data. */

			RootDirectory -> PrimaryType	= 2;
			RootDirectory -> HashTableSize	= 128 - 56;
			RootDirectory -> BitmapFlag	= DOSTRUE;

			DateStamp(&RootDirectory -> LastRootChange);

			if((Len = strlen(DiskName)) > 31)
				Len = 31;

			RootDirectory -> DiskName[0]	= Len;

			CopyMem(DiskName,&RootDirectory -> DiskName[1],Len);

			DateStamp(&RootDirectory -> LastDiskChange);
			DateStamp(&RootDirectory -> CreationDate);

			RootDirectory -> SecondaryType	= 1;

				/* Fill in the bitmap block pointers. */

			for(i = 0 ; i < BitmapBlocks ; i++)
			{
				if(i == 25)
					break;
				else
					RootDirectory -> BitmapPointers[i] = Root + 1 + i + FirstBlock;
			}

				/* If there are more than 25 bitmap blocks, fill in the
				 * pointer to the first bitmap extension block.
				 */

			if(BitmapBlocks > 25)
				RootDirectory -> BitmapExtension = Root + 1 + 25 + FirstBlock;

				/* Calculate the block checksum. */

			RootDirectory -> Checksum = - Checksum((ULONG *)RootDirectory);

				/* And write the block. */

			if(Error = WriteBlock(DevRequest,RootDirectory,Root + FirstBlock))
				return(Error);

				/* The blocks to be occupied by the root block, the bitmap
				 * blocks and the extension blocks are to be marked as
				 * being `in use'.
				 */

			for(i = Root - Reserved ; i < Root - Reserved + 1 + BitmapBlocks + ExtensionBlocks ; i++)
				Bitmap[i / 32] &= Bits[i % 32];

			BitmapBlock = Buffer;

				/* The root block has been written, now continue with the
				 * bitmap blocks.
				 */

			for(i = 0 ; i < BitmapBlocks ; i++)
			{
					/* Copy the bitmap data. */

				CopyMem(&Bitmap[127 * i],&BitmapBlock -> Bitmap[0],127 * sizeof(ULONG));

					/* Calculate the block checksum. */

				BitmapBlock -> Checksum = 0;
				BitmapBlock -> Checksum = - Checksum((ULONG *)BitmapBlock);

					/* And write the block. */

				if(Error = WriteBlock(DevRequest,BitmapBlock,Root + 1 + i + FirstBlock))
					return(Error);
			}

			BitmapExtension = Buffer;

				/* If there are any bitmap extension blocks, create them. */

			if(ExtensionBlocks)
			{
					/* Remember the first blocks to deal with. */

				BitmapCount	= Root + 1 + FirstBlock;
				ExtensionCount	= BitmapCount + BitmapBlocks;

					/* Subtract the first 25 blocks. */

				BitmapBlocks -= 25;

					/* As long as there are any blocks, put them into
					 * the extension blocks.
					 */

				while(BitmapBlocks)
				{
					memset(BitmapExtension,0,TD_SECTOR);

						/* Fill in the bitmap pointers. */

					for(i = 0 ; i < (BitmapBlocks < 127 ? BitmapBlocks : 127) ; i++)
						BitmapExtension -> BitmapPointers[i] = BitmapCount++;

						/* Subtract the bitmap blocks bound to the extension
						 * block.
						 */

					if(BitmapBlocks > 127)
						BitmapBlocks -= 127;
					else
						BitmapBlocks = 0;

						/* If there are any bitmap blocks left, chain
						 * this bitmap extension block to the next one.
						 */

					if(BitmapBlocks)
						BitmapExtension -> BitmapExtension = ExtensionCount + 1;

						/* And write it. */

					if(Error = WriteBlock(DevRequest,BitmapExtension,ExtensionCount++))
						return(Error);
				}
			}

				/* Free the data. */

			FreeMem(Bitmap,sizeof(ULONG) * AllocSize);
			FreeMem(Buffer,TD_SECTOR);

			return(0);
		}

		FreeMem(Buffer,TD_SECTOR);
	}

	return(-1);
}

struct FileSysStartupMsg *
FindDevice(UBYTE *DevName)
{
	UBYTE			*Pointer,i,Name[40];
	struct DeviceNode	*DevInfo;

	Forbid();

	DevInfo = (struct DeviceNode *)BADDR(((struct DosInfo *)BADDR(((struct RootNode *)DOSBase -> dl_Root) -> rn_Info)) -> di_DevInfo);

	while(DevInfo)
	{
		if(DevInfo -> dn_Type == DLT_DEVICE && DevInfo -> dn_Task)
		{
			Pointer = (UBYTE *)BADDR(DevInfo -> dn_Name);

			for(i = 0 ; i < Pointer[0] ; i++)
				Name[i] = Pointer[i + 1];

			Name[Pointer[0]    ] = ':';
			Name[Pointer[0] + 1] = 0;

			if(!Strcmp(Name,DevName))
			{
				Permit();

				return(BADDR(DevInfo -> dn_Startup));
			}
		}

		DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);
	}

	Permit();

	return(NULL);
}

VOID
main(int argc,char **argv)
{
	struct FileSysStartupMsg	*Startup;
	struct DosEnvec			*DosEnvec;

	if(Startup = FindDevice(argv[1]))
	{
		struct IOExtTD	*DevRequest;
		struct MsgPort	*DevPort;

		if(DevPort = CreatePort(NULL,0))
		{
			if(DevRequest = (struct IOExtTD *)CreateExtIO(DevPort,sizeof(struct IOExtTD)))
			{
				if(!OpenDevice(&((UBYTE *)BADDR(Startup -> fssm_Device))[1],Startup -> fssm_Unit,DevRequest,Startup -> fssm_Flags))
				{
					printf("Opened \"%s\" unit %d.\n",&((UBYTE *)BADDR(Startup -> fssm_Device))[1],Startup -> fssm_Unit);

					DosEnvec = (struct DosEnvec *)BADDR(Startup -> fssm_Environ);

					DevRequest -> iotd_Req . io_Command	= TD_MOTOR;
					DevRequest -> iotd_Req . io_Length	= 1;

					DoIO(DevRequest);

					EraseDisk("Blank",DevRequest,DosEnvec -> de_LowCyl * DosEnvec -> de_BlocksPerTrack * DosEnvec -> de_Surfaces,(DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1) * DosEnvec -> de_BlocksPerTrack * DosEnvec -> de_Surfaces,DosEnvec -> de_Reserved,DosEnvec -> de_DosType);

					DevRequest -> iotd_Req . io_Command	= TD_MOTOR;
					DevRequest -> iotd_Req . io_Length	= 0;

					DoIO(DevRequest);

					CloseDevice(DevRequest);
				}
				else
					printf("Failed to open \"%s\" unit %d.\n",&((UBYTE *)BADDR(Startup -> fssm_Device))[1],Startup -> fssm_Unit);

				DeleteExtIO(DevRequest);
			}
			else
				puts("Failed to create IORequest.");

			DeletePort(DevPort);
		}
		else
			puts("Failed to create MsgPort.");
	}
	else
		printf("Unable to find device \"%s\".\n",argv[1]);
}
