/*
 *	HC: device
 *
 */

#include "hc.h"

#define BTOC(x) ((void *)((ULONG)(x) << 2))
#define CTOB(x) ((BPTR)((ULONG)(x) >> 2))

#define JOUR	5131
#define MINUTE	1051
#define TICK	(TICKS_PER_SECOND * 53)


struct List ReadList;
struct Process *Process;
DB	struct DebugInfo dbi;
DB	struct Library *DOSBase;


void main (void)
{
DB	unsigned char temp[256];
	struct DosPacket  *Packet = NULL;
	struct DeviceNode *DevNode = NULL;
	struct DeviceList *Volume = NULL;
	struct FileLock *MasterLock[NUM_FILES] =
	{
		0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L
	};

DB	DOSBase = OpenLibrary ("dos.library", 0L);
DB	if (NULL == DOSBase)
DB		goto quit;
 
	/*
	 *	Get startup message.
	 */

	Process = (struct Process *)FindTask (0L);
	Packet = WaitPacket (Process);

	/*
	 *	We need to fill the dn_Task to keep DOS from launching
	 *	us again and again.
	 */

	DevNode = BTOC (Packet->dp_Arg3);
	DevNode->dn_Task = &Process->pr_MsgPort;

	/*
	 *	Open debug...
	 */

DB	dbi.HCProc = Process;
DB	dbi.HCSig = AllocSignal (-1);
DB	dbi.OutProc = StartProcess ("HC Debug Writer", Outer, &dbi, 10);
DB	if (NULL == dbi.OutProc)
DB		goto quit;

	/*
	 *	Create a bogus volume node.
	 */

	Volume = AllocMem (sizeof (struct DeviceNode), MEMF_PUBLIC);
	if (Volume == NULL)
		goto quit;

	CopyMem (DevNode, Volume, sizeof (struct DeviceNode));
	Volume->dl_VolumeDate.ds_Days = JOUR;
	Volume->dl_VolumeDate.ds_Minute = MINUTE;
	Volume->dl_VolumeDate.ds_Tick = TICK;
	Volume->dl_LockList = NULL;
	Volume->dl_DiskType = DLT_VOLUME;

	/*
	 *	Get the locks.
	 */

	{
		UWORD i;
		for (i = 0; i < NUM_FILES; i++)
		{
			MasterLock[i] = AllocMem (sizeof (struct FileLock), MEMF_PUBLIC);
			if (MasterLock[i] == NULL)
			{
DB				Say ("Can't get locks.\n");
				goto quit;
			}
			if (i)
			{
				MasterLock[i]->fl_Link = CTOB(MasterLock[i - 1]);
			}
			else
			{
				MasterLock[i]->fl_Link = NULL;
			}
			MasterLock[i]->fl_Key = i;
			MasterLock[i]->fl_Access = SHARED_LOCK;
			MasterLock[i]->fl_Task = &Process->pr_MsgPort;
			MasterLock[i]->fl_Volume = CTOB(Volume);
		}
	}

	Volume->dl_LockList = CTOB (MasterLock[NUM_FILES-1]);

	/*
	 *	Setup data functions.
	 */

	if (!OpenData ())
	{
DB		Say ("Can't setup data.\n");
		goto quit;
	}

	/*
	 *	Get the serial device.
	 */

	if (!OpenSer ())
	{
DB		Say ("Can't get serial.\n");
		goto quit;
	}

	InitRead ();

	/*
	 *	Initialize the list of readers.
	 */

	NewList (&ReadList);

	/*
	 *	Everything went fine...
	 */

	Packet->dp_Res1 = DOSTRUE;
	ReturnPacket (Packet, Process);

	/*
	 *	Process all request !
	 */

DB	Say ("Ready to process.\n\n");

	while (1)
	{
		Packet = WaitPacket (Process);
		Packet->dp_Res1 = DOSTRUE;

		switch (Packet->dp_Type)
		{
			case ACTION_FINDUPDATE:
			case ACTION_FINDINPUT:
			case ACTION_FINDOUTPUT:
			{
				if (Packet->dp_Arg2 == ROOT || BTOC(Packet->dp_Arg2) == MasterLock[ROOT])
				{
					struct FileHandle *File = BTOC(Packet->dp_Arg1);
					UWORD Name = WhichFile (BTOC (Packet->dp_Arg3));

					if (Name > 0 && Name < NUM_FILES)
					{
DB						sprintf (temp, "Opening file `%s'.\n", GetName (Name) + 1);

						File->fh_Arg1 = Name;
						File->fh_Port = (struct MsgPort *)DOSTRUE;
						File->fh_Type = &Process->pr_MsgPort;
					}
					else
					{
DB						strcpy (temp, "Opening non-existant file.\n");

						Packet->dp_Res1 = DOSFALSE;
						Packet->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
					}
				}
				else
				{
DB					strcpy (temp, "Can't open a file: invalid lock.\n");

					Packet->dp_Res1 = DOSFALSE;
					Packet->dp_Res2 = ERROR_INVALID_LOCK;
				}
				break;
			}
			case ACTION_READ:
			{
				Packet->dp_Res1 = GetData (Packet->dp_Arg1, (UBYTE *)Packet->dp_Arg2, Packet->dp_Arg3);

DB				sprintf (temp, "Reading %d byte(s) from file `%s' and getting %d.\n", (UWORD)Packet->dp_Arg3, GetName (Packet->dp_Arg1) + 1, (UWORD)Packet->dp_Res1);

				if (Packet->dp_Res1 == -1)
				{
DB					Say ("Adding to waiter list.\n");

					AddTail (&ReadList, &Packet->dp_Link->mn_Node);
					continue;
				}
				break;
			}
			case ACTION_WRITE:
			{
DB				sprintf (temp, "Writing %d byte(s) to file `%s'.\n", (UWORD)Packet->dp_Arg3, GetName (Packet->dp_Arg1) + 1);

				Packet->dp_Res1 = PutData (Packet->dp_Arg1, (UBYTE *)Packet->dp_Arg2, Packet->dp_Arg3);
				break;
			}
			case ACTION_SEEK:
			{
DB				sprintf (temp, "Seeking file `%s'.\n", GetName (Packet->dp_Arg1) + 1);

				Packet->dp_Res1 = 0;
				break;
			}
			case ACTION_LOCATE_OBJECT:
			{
				if (Packet->dp_Arg1 == ROOT || BTOC(Packet->dp_Arg1) == MasterLock[ROOT])
				{
					UWORD FileNum = WhichFile (BTOC (Packet->dp_Arg2));


					if (FileNum < NUM_FILES)
					{
DB						sprintf (temp, "Locating file `%s'.\n", GetName (FileNum) + 1);

						Packet->dp_Res1 = CTOB(MasterLock[FileNum]);
					}
					else
					{
DB						strcpy (temp, "Locating non-existant file.\n");

						Packet->dp_Res1 = DOSFALSE;
						Packet->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
					}
				}
				else
				{
DB					strcpy (temp, "Trying to locate from a file.\n");

					Packet->dp_Res1 = DOSFALSE;
					Packet->dp_Res2 = ERROR_INVALID_LOCK;
				}
				break;
			}
			case ACTION_PARENT:
			{
DB				strcpy (temp, "Finding parent.\n");
				if (Packet->dp_Arg1 == ROOT || BTOC(Packet->dp_Arg1) == MasterLock[ROOT])
				{
					Packet->dp_Res1 = 0;
					Packet->dp_Res2 = 0 /* was: ERROR_DIR_NOT_FOUND */;
				}
				else
				{
					Packet->dp_Res1 = CTOB(MasterLock[ROOT]);
				}
				break;
			}
			case ACTION_COPY_DIR:
			{
DB				strcpy (temp, "Duplicating lock.\n");

				Packet->dp_Res1 = Packet->dp_Arg1;
				break;
			}
			case ACTION_EXAMINE_OBJECT:
			{
				struct FileLock *Lock = BTOC(Packet->dp_Arg1);
				struct FileInfoBlock *Info = BTOC(Packet->dp_Arg2);

				if (Lock == ROOT)
					Lock = MasterLock[ROOT];

DB				sprintf (temp, "Examining file `%s'.\n", GetName (Lock->fl_Key));
				Info->fib_DiskKey = Lock->fl_Key;
				if (Lock == MasterLock[ROOT])
				{
					Info->fib_DirEntryType = 1;
					Info->fib_EntryType = 1;
				}
				else
				{

					Info->fib_DirEntryType = -1;
					Info->fib_EntryType = -1;
				}
				strcpy (Info->fib_FileName, ShowName (Lock->fl_Key));
				Info->fib_Protection = 0;
				Info->fib_Size = DataSize (Lock->fl_Key);
				Info->fib_NumBlocks = Info->fib_Size;
				Info->fib_Date.ds_Days = JOUR;
				Info->fib_Date.ds_Minute = MINUTE;
				Info->fib_Date.ds_Tick = TICK;
				strcpy (Info->fib_Comment, GetComment (Lock->fl_Key));
				break;
			}
			case ACTION_EXAMINE_NEXT:
			{
				struct FileInfoBlock *Info = BTOC(Packet->dp_Arg2);

				if (Packet->dp_Arg1 == ROOT || BTOC(Packet->dp_Arg1) == MasterLock[ROOT])
				{
					if (Info->fib_DiskKey < NUM_FILES-1)
					{
DB						sprintf (temp, "Examining next file: `%s'.\n", GetName (Info->fib_DiskKey + 1) + 1);

						Info->fib_DiskKey += 1;
						Info->fib_DirEntryType = -1;
						Info->fib_Protection = 0;
						Info->fib_EntryType = -1;
						strcpy (Info->fib_FileName, ShowName (Info->fib_DiskKey));
						Info->fib_Size = DataSize (Info->fib_DiskKey);
						Info->fib_NumBlocks = Info->fib_Size;
						Info->fib_Date.ds_Days = JOUR;
						Info->fib_Date.ds_Minute = MINUTE;
						Info->fib_Date.ds_Tick = TICK;
						strcpy (Info->fib_Comment, GetComment (Info->fib_DiskKey));
					}
					else
					{
DB						strcpy (temp, "No more entry.\n");

						Packet->dp_Res1 = DOSFALSE;
						Packet->dp_Res2 = ERROR_NO_MORE_ENTRIES;
					}
				}
				else
				{
DB					strcpy (temp, "Invalid Examine next.\n");

					Packet->dp_Res1 = DOSFALSE;
					Packet->dp_Res2 = ERROR_INVALID_LOCK;
				}
				break;
			}
			case ACTION_DISK_INFO:
			{
				Packet->dp_Arg2 = Packet->dp_Arg1;
			}
			case ACTION_INFO:
			{
				struct InfoData *Disk = BTOC (Packet->dp_Arg2);

DB				strcpy (temp, "Getting info about disk.\n");

				Disk->id_NumSoftErrors = 0;
				Disk->id_UnitNumber = 0;
				Disk->id_DiskState = ID_VALIDATED;
				Disk->id_NumBlocks = 4096 * 5;
				Disk->id_NumBlocksUsed = 0;
				Disk->id_BytesPerBlock = 1;
				Disk->id_DiskType = ID_DOS_DISK;
				Disk->id_VolumeNode = CTOB (Volume);
				Disk->id_InUse = 0;
				break;
			}
			case ACTION_CURRENT_VOLUME:
			{
DB				strcpy (temp, "Finding current volume.\n");

				Packet->dp_Res1 = CTOB(Volume);
				break;
			}
			case ACTION_INHIBIT:
			{
				if (Packet->dp_Arg1 == DOSTRUE)
				{
DB					strcpy (temp, "Inhibiting on.\n");

					CloseSer ();
				}
				else
				{
DB					strcpy (temp, "Inhibiting off.\n");

					OpenSer ();
				}
				break;
			}
			case ACTION_FLUSH:
			{
DB				strcpy (temp, "Flushing device.\n");

				FlushWriteSer ();
				break;
			}
			case ACTION_SET_FILE_SIZE:
			{
DB				sprintf (temp, "Truncating file `%s'.\n", GetName (Packet->dp_Arg1) + 1);

				break;
			}
			case ACTION_END:
			{
DB				sprintf (temp, "Closing file `%s'.\n", GetName (Packet->dp_Arg1) + 1);

				break;
			}
			case ACTION_FREE_LOCK:
			{
DB				strcpy (temp, "Freeing lock.\n");

				break;
			}
			case ACTION_SET_PROTECT:
			{
DB				strcpy (temp, "Setting protection.\n");

				break;
			}
			case ACTION_SET_COMMENT:
			{
DB				strcpy (temp, "Setting comment.\n");

				break;
			}
			case ACTION_SET_DATE:
			{
DB				strcpy (temp, "Setting date.\n");

				break;
			}
			case ACTION_DIE:
			{
DB				Say ("Arrrrrrgh !\n");
				goto quit;
				break;
			}
			default:
			{
DB				sprintf (temp, "Unknown packet type: %d.\n", (UWORD)Packet->dp_Type);

				Packet->dp_Res1 = DOSFALSE;
				Packet->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
				break;
			}
		}

DB		Say (temp);
		ReturnPacket (Packet, Process);
	}

quit:

	{
		UWORD i = 0;
		while (i < NUM_FILES)
			if (MasterLock[i])
				FreeMem (MasterLock[i], sizeof (struct FileLock));
	}
	CloseSer ();
	CloseData ();
DB	if (dbi.OutProc)
DB	{
DB		KillProcesses ();
DB		WaitProcesses ();
DB	}
DB	if (DOSBase)	CloseLibrary (DOSBase);

	Packet->dp_Res1 = DOSFALSE;
	Forbid ();
	ReturnPacket (Packet, Process);
}


DB	void Say (char *text)
DB	{
DB		strcpy (dbi.Msg, text);
DB		Signal (dbi.OutProc, 1L << dbi.OutSig);
DB		Wait (1L << dbi.HCSig);
DB	}
