#include "DFC5.h"

#define ESC 0x1B
#define RETURN 0x0D

/*
 * Mask for EnableGadgets
 */

#define ALLMASK 0x1FFFF
#define COPYMASK 0x03D00
#define AGAINMASK 0x03D00
#define FORMATMASK 0x03700

#define ALLMSGS 0x3F

static char Key2Gadget[39] = { DF0S, DF1S, DF2S, DF3S, DF0D, DF1D, DF2D, DF3D, -1, -1, -1, -1, -1, -1, -1, -1, -1, AUTO, BUFFER, -1, DATE, -1, FORMAT, GO, -1, -1, -1, -1, -1, -1, FFS, -1, -1, -1, REPEAT, STOP, TALK, -1, VERIFY };
static char NoBufferMsg[] = "Sorry, I need a buffer.";
static char NoDiskMsg[] = "Can't open disk.";
static char NoDestMsg[] = "Sorry, I need a destination.";
static char NoSourceMsg[] = "Sorry, I need a source.";
static char NoDiskPresentMsg[] = "No disk in unit x.";
static char WriteProtectedMsg[] = "Disk in unit x is write protected.";
static char GfxName[] = "graphics.library";

extern struct MsgPort *MainPort;

static struct Window *Window;
static char *FormatBuffer, MsgLocked[6],
				Source = -1, Dest, ActualSource, ActualDest,
				Verify=1, Buffer, BufferUsed, BufferTrack,
				Date=1, UseFFS, Fuga, Copying, Formatting,
				Passes = 1, CurrentPass, CurrentStartTrack,
				CurrentEndTrack, Track, TracksPerPass = 80;

/*
 * This routine sends an internal message with given parameters and locks it.
 */

void SendAndLockMsg(int Unit, int Action, int n) {

	SendMsg(Unit, Action, n);
	MsgLocked[Unit] = 1;
}

/*
 * The same, but the message is simply re-sent (for retrying errors).
 */

void ReSendAndLockMsg(struct IMsg *Message) {

	SendAndLockMsg(Message->im_ID, Message->im_Action, Message->im_n);
}

/*
 * Check if units in the Mask have locked msgs.
 */

int NoLockedMsgs(int Mask) {

	register int i;

	for(i=0; i<6; i++) if (MsgLocked[i] && (DEST(i) & Mask)) return(FALSE);
	return(TRUE);
}

/*
 * Here we stop any operation. If we weren't copying, and the buffer is on,
 * we wipe the progress bar and reset the pass counter.
 */

void Stop(void) {
	if (Copying) {
		if (Copying == 'G' && Buffer)
			if (--CurrentPass<0) CurrentPass = Passes-1;
		Copying = 0;
		Track = CurrentEndTrack-1;
		Beep(2400);
	}
	else {
		CurrentPass = Passes-1;
		if (Buffer) WipeProgress(Window, 0);
	}
}

/*
 * Here we check if some fatal error was encountered.
 */

int FatalErrors(struct IMsg *Message) {
	if (Message->im_RC == NO_DISK) {
		NoDiskPresentMsg[16] = 48+Message->im_ID;
		Acknowledge(NoDiskPresentMsg);
		return(1);
	}
	if (Message->im_RC == WRITE_PROTECTED) {
		WriteProtectedMsg[13] = 48+Message->im_ID;
		Acknowledge(WriteProtectedMsg);
		return(1);
	}
	return(0);
}


/*
 * The big thing!
 * Apart from the open-all-stuff/close-all-stuff parts, this is simply a
 * preposterously huge event loop.
 */

void _main(void) {

	register int i;
	register char *b;
	register struct Gadget *G;

	struct IMsg *Message;
	struct IntuiMessage *Msg;

	int Op, Class, Code;

	if ((GfxBase = (void *)OpenLibrary(GfxName,36))==NULL) {
		if ((GfxBase = (void *)OpenLibrary(GfxName,33))==NULL) goto GameOver;
		ReverseBorderColors();
	}

	if (!SetUpPorts() ||
		(IntuitionBase = (void *)OpenLibrary("intuition.library",33))==NULL ||
		(Window = SetUpWindow())==NULL)
		goto GameOver;

	SetUpAudio();

	FOREVER {

		do {

			Msg = (void *)GetMsg(Window->UserPort);
			Message = (void *)GetMsg(MainPort);

			if (Msg) {

				Code = Msg->Code ;
				Class = Msg->Class;
				G = (struct Gadget *)(Msg->IAddress);
				ReplyMsg((void *)Msg) ;

				switch(Class) {
					case CLOSEWINDOW: Fuga = 1;
											break;

					case GADGETUP:
					case GADGETDOWN:
					case VANILLAKEY:
						if (Class == VANILLAKEY) {
							Op = toupper(Code);
							if (Op>='0' && Op<='V' && Key2Gadget[Op]>=0) G = GAddr(Key2Gadget[Op-48]);
							else G = NULL;
						}
						else Op = G->GadgetID;

						switch(Op) {
							case RETURN:
											Op = Buffer ? 'R' : 'G';
							case 'F':
							case 'R':
							case 'G':	if (Copying || !NoLockedMsgs(ALLMSGS) || (Op == 'F' && !(FormatBuffer = AllocMem(TRACKSIZE, MEMF_PUBLIC)))) break;
											if (Op == 'G' && Source<0) {
												Acknowledge(NoSourceMsg);
												break;
											}
											if (((Op == 'G' && INDEST(Source)) || Op == 'R') && !Buffer) {
												Acknowledge(NoBufferMsg);
												break;
											}
											if ((Op == 'R' || Op == 'F' || (Op == 'G' && !Buffer)) && !Dest) {
												Acknowledge(NoDestMsg);
												break;
											}
											ActualDest = Dest;
											switch(Copying = Op) {
												case 'G':
													EnableGadgets(Window, COPYMASK);
													ActualSource = Source;
													if (Buffer) ActualDest = DEST(4);
													if (++CurrentPass == Passes) CurrentPass = 0;
													break;
												case 'R':
													EnableGadgets(Window, AGAINMASK);
													ActualSource = 4;
													break;
												case 'F':
													EnableGadgets(Window, FORMATMASK);
													ActualSource = 5;
													break;
											}

											if (Formatting = (Copying == 'F')) {
												CurrentStartTrack = 79;
												CurrentEndTrack = 0;
											}
											else {
												CurrentStartTrack = 79-CurrentPass*TracksPerPass;
												CurrentEndTrack = max(0, CurrentStartTrack-TracksPerPass+1);
											}
											Track = CurrentStartTrack;
											WipeProgress(Window, 79-CurrentStartTrack);
											for(i=0; i<5; i++) if (INACTUALDEST(i) || i == ActualSource) SendAndLockMsg(i, INIT, i == ActualSource && Date && !Formatting && CurrentPass == 0 ? -1 : CurrentStartTrack);
											BufferUsed = DEST(ActualSource);
											break;

							case 'S':	Stop();
											break;

							case ESC:
							case 'Q':	Fuga = 1;
											break;


							case 'B':
							case 'V':
							case 'D':
							case 'A':
							case 'N':
							case 'T':	if (!G) break;
											if (Class == VANILLAKEY) ToggleGadget(Window, G);
											i =  ((G->Flags & SELECTED) != 0);

											switch(Op) {
												case 'V':	Verify = i;
																break;
												case 'D':	Date = i;
																break;
												case 'A':	break;
												case 'N':	UseFFS = i;
																break;
												case 'T':	if (i) {
																	if (!OpenVoice()) SelectGadget(Window, G, FALSE);
																}
																else CloseVoice();
																break;
												case 'B':	if (i) {
																	if (Passes = OpenDiskTask(4)) {
																		Buffer = 1;
																	}
																	else {
																		SelectGadget(Window, G, FALSE);
																		Acknowledge("Not enough memory.");
																	}
																}
																else {
																	CloseDiskTask(4);
																	Buffer = 0;
																}
																if (!Buffer) {
																	Passes = 1;
																}
																TracksPerPass = 80/Passes+(Passes==3);
																CurrentPass = Passes-1;
																break;
											}
											break;
							case '0':
							case '1':
							case '2':
							case '3':	Op -= '0';
											if (!G) break;
											if (Class == VANILLAKEY) ToggleGadget(Window, G);

											if ((i = (G->Flags & SELECTED)) && OpenDiskTask(Op)) {
												if (Source == Op) break;
												if (Source>=0) {
													SelectGadget(Window, GAddr(DF0S)+Source, FALSE);
													if (!INDEST(Source)) CloseDiskTask(Source);
												}
												Source = Op;
											}
											else {
												SelectGadget(Window, G, FALSE);
												if (i) Acknowledge(NoDiskMsg);
												if (!INDEST(Op)) CloseDiskTask(Op);
												if (Op == Source) Source = -1;
											}

											break;

							case '4':
							case '5':
							case '6':
							case '7':	Op -= '4';
											if (!G) break;
											if (Class == VANILLAKEY) ToggleGadget(Window, G);

											if ((i = (G->Flags & SELECTED)) && OpenDiskTask(Op)) Dest |= DEST(Op);
											else {
												Dest &= ~DEST(Op);
												SelectGadget(Window, G, FALSE);
												if (i) Acknowledge(NoDiskMsg);
												if (Op != Source) CloseDiskTask(Op);
											}
											break;
						}
						break;
				}
			}

			if (Message) {

				MsgLocked[Message->im_ID] = 0;

				switch(Message->im_Action) {
					case INIT:	if (Message->im_ID == ActualSource && Message->im_RC == NOT_DOS) Acknowledge("This is not a DOS disk.");
									break;

					case READ_TRACK:
							if (Copying && Message->im_RC == READ_ERROR && ProblemsWithUnit("Read", Message->im_ID, Message->im_n)) {
								ReSendAndLockMsg(Message);
								break;
							}
							if (FatalErrors(Message)) Stop();

							b = Message->im_p;
							BufferTrack = Message->im_n;
							BufferUsed = ActualDest;
							if (Formatting && Message->im_n>=0) MakeFormatData(Message->im_n, b = FormatBuffer, UseFFS);
							break;

					case WRITE_TRACK:
					case WRITE_AND_VERIFY_TRACK:
							if (Copying &&
								((Message->im_RC == WRITE_ERROR && ProblemsWithUnit("Write", Message->im_ID, Message->im_n)) ||
								(Message->im_RC == VERIFY_ERROR && ProblemsWithUnit("Verify", Message->im_ID, Message->im_n)))) {
								ReSendAndLockMsg(Message);
								break;
							}
							if (FatalErrors(Message)) Stop();

							break;

					case STOP_MOTOR:	if (Message->im_ID == ActualSource) {
												BufferUsed = ActualDest;
												BufferTrack = CurrentEndTrack-1;
											}
											else if (NoLockedMsgs(ActualDest)) {
												EnableGadgets(Window, ALLMASK);
												UpdateProgress(Window);
												if (FormatBuffer) {
													FreeMem(FormatBuffer, TRACKSIZE);
													FormatBuffer = NULL;
												}
												Copying = 0;
											}
											break;
				}


				if (NoLockedMsgs(ActualDest))
					for(i=0; i<5; i++)
						if (DEST(i) & BufferUsed & ActualDest) {
							BufferUsed &= ~DEST(i);
							if ((Date || Formatting) && BufferTrack==40) UpdateRootBlock(b);
							CopyBuffer(b, i, BufferTrack % TracksPerPass);
							SendAndLockMsg(i, (Copying && BufferTrack>=CurrentEndTrack) ? (Verify ? WRITE_AND_VERIFY_TRACK : WRITE_TRACK) : STOP_MOTOR, BufferTrack);
						}

				if (!BufferUsed) {
					if (Copying && BufferTrack>=CurrentEndTrack) DrawProgress(Window, 79-BufferTrack, Buffer && Copying == 'G' ? 1 : 3);
					BufferUsed = DEST(ActualSource);
					if (BufferTrack == CurrentEndTrack+4) Beep(3000);
					if (Copying && BufferTrack == CurrentEndTrack-1) {
						Beep(4000);
						if (!Buffer || Formatting) Say("Operation completed.");
						else Say("Pass completed.");
					}
				}

				if (!MsgLocked[ActualSource] && (BufferUsed & DEST(ActualSource)) && Track >= CurrentEndTrack-1) {
					SendAndLockMsg(ActualSource, (Copying && Track>=CurrentEndTrack) ? READ_TRACK : STOP_MOTOR, Track--);
				}
			}
		} while(Msg || Message);

		if (Fuga && NoLockedMsgs(ALLMSGS)) break;
		Wait(1 << Window->UserPort->mp_SigBit | 1 << MainPort->mp_SigBit);
	}

GameOver:
	if (Window) CloseWindow(Window);
	if (IntuitionBase) CloseLibrary((void *)IntuitionBase);
	if (GfxBase) CloseLibrary((void *)GfxBase);
	for(i=0; i<5; i++) CloseDiskTask(i);
	CloseAudio();
	CloseVoice();
	ClosePorts();
}
