#include <intuition/intuitionbase.h>
#include <libraries/commodities.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dos.h>

#include <clib/commodities_protos.h>
#include <clib/intuition_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>

#define SIG_BREAK	SIGBREAKF_CTRL_C
#define SIG_NOTICE	SIGBREAKF_CTRL_D
#define SIG_ON		SIGBREAKF_CTRL_E
#define SIG_OFF		SIGBREAKF_CTRL_F

#define NUM_PATCHES	(sizeof(PatchTable) / sizeof(struct PatchInfo))

#define JMP_ABS		0x4EF9

struct Wedge
{
	UWORD	Command;
	APTR	Address;
	UWORD	Pad;
};

struct PatchInfo
{
	APTR		 NewRoutine;
	LONG		 Offset;
	ULONG		*Destination;
};

struct WindowNode
{
	struct MinNode	 Node;
	struct Window	*Window;
	UWORD		*Pointer;
	BYTE		 Height,
			 Width;
	BYTE		 XOffset,
			 YOffset;
	BYTE		 Off;
};

extern ULONG __far		 LVOClearPointer,
				 LVOSetPointer,
				 LVOOpenWindow,
				 LVOOpenWindowTagList,
				 LVOCloseWindow;

extern VOID __stdargs		 StackOldSetPointer(struct Window *Window,UWORD *Pointer,WORD Height,WORD Width,WORD XOffset,WORD YOffset);
extern VOID 			 NewSetPointer(VOID);

APTR				 OldSetPointer;

VOID			(* __asm OldClearPointer)(register __a0 struct Window *,register __a6 struct IntuitionBase *);
struct Window *		(* __asm OldOpenWindowTagList)(register __a0 struct NewWindow *,register __a1 struct TagItem *,register __a6 struct IntuitionBase *);
struct Window *		(* __asm OldOpenWindow)(register __a0 struct NewWindow *,register __a6 struct IntuitionBase *);
VOID			(* __asm OldCloseWindow)(register __a0 struct Window *,register __a6 struct IntuitionBase *);

LONG __saveds			 Main(VOID);
VOID __regargs			 UpdateNode(struct WindowNode *Node,struct Window *Window);
struct WindowNode * __regargs	 NewNode(struct Window *Window);

VOID __saveds __stdargs		 BlankerAction(CxMsg *CxMessage,CxObj *CxObject);
VOID				 ShutdownCx(VOID);
BYTE				 SetupCx(VOID);
VOID				 TurnOff(VOID);
VOID				 TurnOn(VOID);

VOID __stdargs __saveds		 StackNewSetPointer(struct Window *Window,UWORD *Pointer,WORD Height,WORD Width,WORD XOffset,WORD YOffset);

VOID __asm __saveds		 NewClearPointer(register __a0 struct Window *Window);
struct Window * __asm __saveds	 NewOpenWindowTagList(register __a0 struct NewWindow *NewWindow,register __a1 struct TagItem *TagList);
struct Window * __asm __saveds	 NewOpenWindow(register __a0 struct NewWindow *NewWindow);
VOID __asm __saveds		 NewCloseWindow(register __a0 struct Window *Window);

struct ExecBase		*SysBase;
struct IntuitionBase	*IntuitionBase;
struct Library		*CxBase;

struct Process		*MainProcess;

struct SignalSemaphore	 WindowSemaphore;
struct List		 WindowList;

ULONG			 UseCount	= 0,
			 BlankCount	= 0;
BYTE			 AllOff		= FALSE;

struct MsgPort		*CxPort;
CxObj			*Broker;

struct NewBroker NewBroker =
{
	NB_VERSION,
	"MouseBlanker",
	"Mouse Pointer Blanker v1.0",
	"Mouse Pointer Blanker",
	NBU_UNIQUE,
	NULL,
	0,NULL,0
};

struct PatchInfo PatchTable[] =
{
	NewClearPointer,	(LONG)&LVOClearPointer,		(ULONG *)&OldClearPointer,
	NewSetPointer,		(LONG)&LVOSetPointer,		(ULONG *)&OldSetPointer,
	NewOpenWindowTagList,	(LONG)&LVOOpenWindowTagList,	(ULONG *)&OldOpenWindowTagList,
	NewOpenWindow,		(LONG)&LVOOpenWindow,		(ULONG *)&OldOpenWindow,
	NewCloseWindow,		(LONG)&LVOCloseWindow,		(ULONG *)&OldCloseWindow
};

UWORD __chip BlankSprite[(2 + 1) * 2] =
{
	0x0000,0x0000,

	0x0000,0x0000,

	0x0000,0x0000
};

LONG __saveds
Main()
{
	LONG Result = RETURN_FAIL;

	SysBase = *(struct ExecBase **)4;

	MainProcess = (struct Process *)SysBase -> ThisTask;

	if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37))
	{
		if(CxBase = OpenLibrary("commodities.library",37))
		{
			if(SetupCx())
			{
				struct Wedge *WedgeTable;

				if(WedgeTable = (struct Wedge *)AllocMem(sizeof(struct Wedge) * NUM_PATCHES,MEMF_ANY|MEMF_CLEAR))
				{
					struct WindowNode	*Node,
								*Next;
					ULONG			 IntuiLock,
								 SignalSet;
					struct Screen		*Screen;
					struct Window		*Window;
					BYTE			 Terminated = FALSE;
					WORD			 i;

					Result = RETURN_OK;

					InitSemaphore(&WindowSemaphore);

					NewList(&WindowList);

					IntuiLock = LockIBase(NULL);

					Screen = IntuitionBase -> FirstScreen;

					while(Screen)
					{
						Window = Screen -> FirstWindow;

						while(Window)
						{
							NewNode(Window);

							Window = Window -> NextWindow;
						}

						Screen = Screen -> NextScreen;
					}

					Forbid();

					UnlockIBase(IntuiLock);

					for(i = 0 ; i < NUM_PATCHES ; i++)
					{
						WedgeTable[i] . Command	= JMP_ABS;
						WedgeTable[i] . Address	= PatchTable[i] . NewRoutine;

						*PatchTable[i] . Destination = (ULONG)SetFunction((struct Library *)IntuitionBase,PatchTable[i] . Offset,(APTR)&WedgeTable[i]);
					}

					CacheClearU();

					Permit();

					while(!Terminated)
					{
						SignalSet = Wait(SIG_BREAK | SIG_ON | SIG_OFF);

						if(SignalSet & SIG_BREAK)
							Terminated = TRUE;

						if(SignalSet & SIG_ON)
							TurnOn();

						if(SignalSet & SIG_OFF)
							TurnOff();
					}

					Forbid();

					for(i = 0 ; i < NUM_PATCHES ; i++)
						WedgeTable[i] . Address	= (APTR)*PatchTable[i] . Destination;

					CacheClearU();

					SetSignal(0,SIG_NOTICE);

					Permit();

					while(UseCount > 0)
						Wait(SIG_NOTICE);

					TurnOn();

					ObtainSemaphore(&WindowSemaphore);

					Node = (struct WindowNode *)WindowList . lh_Head;

					while(Next = (struct WindowNode *)Node -> Node . mln_Succ)
					{
						Remove((struct Node *)Node);

						FreeVec(Node);

						Node = Next;
					}

					ReleaseSemaphore(&WindowSemaphore);
				}

				ShutdownCx();
	                }
		}

		CloseLibrary((struct Library *)IntuitionBase);
	}

	return(Result);
}

VOID __regargs
UpdateNode(struct WindowNode *Node,struct Window *Window)
{
	Node -> Pointer	= Window -> Pointer;
	Node -> Height	= Window -> PtrHeight;
	Node -> Width	= Window -> PtrWidth;
	Node -> XOffset	= Window -> XOffset;
	Node -> YOffset	= Window -> YOffset;
}

struct WindowNode * __regargs
NewNode(struct Window *Window)
{
	struct WindowNode *Node;

	if(Node = (struct WindowNode *)AllocVec(sizeof(struct WindowNode),MEMF_PUBLIC | MEMF_CLEAR))
	{
		Node -> Window = Window;

		UpdateNode(Node,Window);

		AddTail(&WindowList,(struct Node *)Node);
	}

	return(Node);
}

struct WindowNode * __regargs
FindWindow(struct Window *Window)
{
	struct WindowNode	*Node,
				*Next;

	Node = (struct WindowNode *)WindowList . lh_Head;

	while(Next = (struct WindowNode *)Node -> Node . mln_Succ)
	{
		if(Window == Node -> Window)
			return(Node);

		Node = Next;
	}

	return(NULL);
}

VOID __saveds __stdargs
BlankerAction(CxMsg *CxMessage,CxObj *CxObject)
{
	struct InputEvent *Event = (struct InputEvent *)CxMsgData(CxMessage);

	switch(Event -> ie_Class)
	{
		case IECLASS_TIMER:	if(BlankCount++ >= 50)
					{
						BlankCount = 0;

						Signal(MainProcess,SIG_OFF);
					}

					break;

		case IECLASS_RAWKEY:	if(!(Event -> ie_Code & IECODE_UP_PREFIX))
						Signal(MainProcess,SIG_OFF);

					break;

		case IECLASS_NEWPOINTERPOS:
		case IECLASS_POINTERPOS:
		case IECLASS_RAWMOUSE:	Signal(MainProcess,SIG_ON);
					break;

		default:		break;
	}
}

VOID
ShutdownCx()
{
	if(CxPort)
	{
		struct Message *Message;

		if(Broker)
			DeleteCxObjAll(Broker);

		RemPort(CxPort);

		while(Message = GetMsg(CxPort))
			ReplyMsg(Message);

		DeleteMsgPort(CxPort);

		CxPort = NULL;
		Broker = NULL;
	}
}

BYTE
SetupCx()
{
	ShutdownCx();

	if(CxPort = CreateMsgPort())
	{
		CxPort -> mp_Node . ln_Name = NewBroker . nb_Name;

		AddPort(CxPort);

		NewBroker . nb_Port = CxPort;
		NewBroker . nb_Pri  = 0;

		if(Broker = CxBroker(&NewBroker,NULL))
		{
			CxObj *ObjectList;

			ObjectList = CxCustom(BlankerAction,NULL);

			if(!CxObjError(ObjectList))
			{
				AttachCxObj(Broker,ObjectList);

				if(!CxObjError(Broker))
				{
					ActivateCxObj(Broker,TRUE);

					return(TRUE);
				}
			}
		}
	}

	ShutdownCx();

	return(FALSE);
}

VOID
TurnOff()
{
	ObtainSemaphore(&WindowSemaphore);

	if(!AllOff)
	{
		struct WindowNode	*Node,
					*Next;

		Node = (struct WindowNode *)WindowList . lh_Head;

		while(Next = (struct WindowNode *)Node -> Node . mln_Succ)
		{
			if(!Node -> Off)
			{
				UpdateNode(Node,Node -> Window);

				StackOldSetPointer(Node -> Window,BlankSprite,1,16,0,0);

				Node -> Off = TRUE;
			}

			Node = Next;
		}

		AllOff = TRUE;
	}

	ReleaseSemaphore(&WindowSemaphore);
}

VOID
TurnOn()
{
	struct WindowNode	*Node,
				*Next;

	ObtainSemaphore(&WindowSemaphore);

	AllOff = FALSE;

	BlankCount = 0;

	Node = (struct WindowNode *)WindowList . lh_Head;

	while(Next = (struct WindowNode *)Node -> Node . mln_Succ)
	{
		if(Node -> Off)
		{
			if(Node -> Pointer)
				StackOldSetPointer(Node -> Window,Node -> Pointer,Node -> Height,Node -> Width,Node -> XOffset,Node -> YOffset);
			else
				OldClearPointer(Node -> Window,IntuitionBase);

			Node -> Off = FALSE;
		}

		Node = Next;
	}

	ReleaseSemaphore(&WindowSemaphore);
}

VOID __asm __saveds
NewClearPointer(register __a0 struct Window *Window)
{
	struct WindowNode *Node;

	ObtainSemaphore(&WindowSemaphore);

	UseCount++;

	if(Node = FindWindow(Window))
	{
		if(!AllOff)
		{
			OldClearPointer(Window,IntuitionBase);

			Node -> Off = FALSE;
		}
		else
			Node -> Off = TRUE;

		Node -> Pointer = NULL;
	}
	else
		OldClearPointer(Window,IntuitionBase);

	UseCount--;

	Signal(MainProcess,SIG_NOTICE);

	ReleaseSemaphore(&WindowSemaphore);
}

VOID __stdargs __saveds
StackNewSetPointer(struct Window *Window,UWORD *Pointer,WORD Height,WORD Width,WORD XOffset,WORD YOffset)
{
	struct WindowNode *Node;

	ObtainSemaphore(&WindowSemaphore);

	UseCount++;

	if(Node = FindWindow(Window))
	{
		if(!AllOff)
		{
			StackOldSetPointer(Window,Pointer,Height,Width,XOffset,YOffset);

			Node -> Off = FALSE;
		}
		else
			Node -> Off = TRUE;

		Node -> Pointer	= Pointer;
		Node -> Height	= Height;
		Node -> Width	= Width;
		Node -> XOffset	= XOffset;
		Node -> YOffset	= YOffset;
	}
	else
		StackOldSetPointer(Window,Pointer,Height,Width,XOffset,YOffset);

	UseCount--;

	Signal(MainProcess,SIG_NOTICE);

	ReleaseSemaphore(&WindowSemaphore);
}

struct Window * __asm __saveds
NewOpenWindowTagList(register __a0 struct NewWindow *NewWindow,register __a1 struct TagItem *TagList)
{
	struct Window	*Window;
	ULONG		 Signals;

	ObtainSemaphore(&WindowSemaphore);

	UseCount++;

	if(Window = OldOpenWindowTagList(NewWindow,TagList,IntuitionBase))
	{
		NewNode(Window);

		Signals = SIG_NOTICE | SIG_ON;
	}
	else
		Signals = SIG_NOTICE;

	UseCount--;

	Signal(MainProcess,Signals);

	ReleaseSemaphore(&WindowSemaphore);

	return(Window);
}

struct Window * __asm __saveds
NewOpenWindow(register __a0 struct NewWindow *NewWindow)
{
	struct Window	*Window;
	ULONG		 Signals;

	ObtainSemaphore(&WindowSemaphore);

	UseCount++;

	if(Window = OldOpenWindow(NewWindow,IntuitionBase))
	{
		NewNode(Window);

		Signals = SIG_NOTICE | SIG_ON;
	}
	else
		Signals = SIG_NOTICE;

	UseCount--;

	Signal(MainProcess,Signals);

	ReleaseSemaphore(&WindowSemaphore);

	return(Window);
}

VOID __asm __saveds
NewCloseWindow(register __a0 struct Window *Window)
{
	struct WindowNode *Node;

	ObtainSemaphore(&WindowSemaphore);

	UseCount++;

	if(Node = FindWindow(Window))
	{
		Remove((struct Node *)Node);

		FreeVec(Node);
	}

	OldCloseWindow(Window,IntuitionBase);

	UseCount--;

	Signal(MainProcess,SIG_NOTICE);

	ReleaseSemaphore(&WindowSemaphore);
}
