#include <exec/exec.h>
#include <devices/trackdisk.h>
#include <devices/scsidisk.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <stddef.h>
#include "SDI_compiler.h"
#include "SCSIDebug_rev.h"

#define PROGNAME "SCSIDebug"
#define TEMPLATE "DEVICE/A,UNIT/N/A"
#ifdef __amigaos4__
#define dbug(x) DebugPrintF x
#else
#define dbug(x) kprintf x
#endif
#define _LVOBeginIO (-30)

const char USED verstag[] = VERSTAG;

struct {
	const char *device;
	ULONG *unit;
} args;

struct Unit *unit;
#ifdef __amigaos4__
void (*oldBeginIO) (struct DeviceManagerInterface *Self, struct IOStdReq *io);
void newBeginIO (struct DeviceManagerInterface *Self, struct IOStdReq *io);
#else
void (*oldBeginIO) (REG(a1, struct IOStdReq *io), REG(a6, struct Library *base));
void newBeginIO (REG(a1, struct IOStdReq *io), REG(a6, struct Library *base));
#endif

int main () {
	APTR rdargs = NULL;
	int rc = RETURN_FAIL;
	struct MsgPort *mp = NULL;
	struct IOStdReq *io = NULL;
	BYTE error;
	struct Library *base = NULL;
#ifdef __amigaos4__
	struct Interface *interface = NULL;
#endif
	
	rdargs = ReadArgs(TEMPLATE, (APTR)&args, NULL);
	if (!rdargs) {
		PrintFault(IoErr(), PROGNAME);
		goto out;
	}
	
#ifdef __amigaos4__
	mp = AllocSysObject(ASOT_PORT, NULL);
	io = AllocSysObjectTags(ASOT_IOREQUEST,
		ASOIOR_Size,		sizeof(struct IOStdReq),
		ASOIOR_ReplyPort,	mp,
		TAG_END);
#else
	mp = CreateMsgPort();
	io = CreateIORequest(mp, sizeof(struct IOStdReq));
#endif
	if (!io) {
		PrintFault(ERROR_NO_FREE_STORE, PROGNAME);
		goto out;
	}

	error = OpenDevice(args.device, *args.unit, (struct IORequest *)io, 0);
	if (error) {
		io->io_Device = NULL;
		Printf("%s:%ld: OpenDevice returned error %ld\n", args.device, *args.unit, error);
		goto out;
	}
	
	base = &io->io_Device->dd_Library;
#ifdef __amigaos4__
	if (base->lib_ABIVersion != LIBABI_68K) {
		interface = GetInterface(base, "__device", 1, NULL);
		if (!interface) {
			Printf("%s:%ld: Failed to obtain device manager interface\n", args.device, *args.unit);
			goto out;
		}
	}
#endif
	
	rc = RETURN_OK;

	unit = io->io_Unit;
#ifdef __amigaos4__
	if (base->lib_ABIVersion != LIBABI_68K) {
		oldBeginIO = SetMethod(interface, offsetof(struct DeviceManagerInterface, BeginIO),
			(APTR)newBeginIO);
	} else {
		Printf("FIXME: implement patching of m68k devices\n");
		goto out;
	}
#else
	oldBeginIO = SetFunction(base, _LVOBeginIO, (APTR)newBeginIO);
#endif

	Printf("oldBeginIO: %08lx\nnewBeginIO: %08lx\n", oldBeginIO, newBeginIO);
	
	Wait(SIGBREAKF_CTRL_C);
	
#ifdef __amigaos4__
	if (base->lib_ABIVersion != LIBABI_68K) {
		SetMethod(interface, offsetof(struct DeviceManagerInterface, BeginIO),
			(APTR)oldBeginIO);
	} else {
		// ...
	}
#else
	SetFunction(base, _LVOBeginIO, (APTR)oldBeginIO);
#endif

out:
#ifdef __amigaos4__
	DropInterface(interface);
#endif
	if (io && io->io_Device) {
		CloseDevice((struct IORequest *)io);
	}
#ifdef __amigaos4__
	FreeSysObject(ASOT_IOREQUEST, io);
	FreeSysObject(ASOT_PORT, mp);
#else
	DeleteIORequest(io);
	DeleteMsgPort(mp);
#endif
	FreeArgs(rdargs);
	return rc;
}

#ifdef __amigaos4__
void newBeginIO (struct DeviceManagerInterface *Self, struct IOStdReq *io) {
#else
void newBeginIO (REG(a1, struct IOStdReq *io), REG(a6, struct Library *base)) {
#endif
	if (io->io_Unit == unit && io->io_Command == HD_SCSICMD) {
		struct SCSICmd *scsi = io->io_Data;
		int i;
		dbug(("scsi_Data: 0x%08lx\n", scsi->scsi_Data));
		dbug(("scsi_Length: %ld\n", scsi->scsi_Length));
		dbug(("scsi_Command:"));
		for (i = 0; i < scsi->scsi_CmdLength; i++) {
			dbug((" %02lx", scsi->scsi_Command[i]));
		}
		dbug(("\nscsi_CmdLength: %ld\n", scsi->scsi_CmdLength));
		dbug(("scsi_Flags: 0x%02lx\n", scsi->scsi_Flags));
		dbug(("scsi_Status: 0x%02lx\n", scsi->scsi_Status));
		dbug(("scsi_SenseData: 0x%08lx\n", scsi->scsi_SenseData));
		dbug(("scsi_SenseLength: %ld\n", scsi->scsi_SenseLength));
	}
#ifdef __amigaos4__
	oldBeginIO(Self, io);
#else
	oldBeginIO(io, base);
#endif
}
