#include <stdio.h>
#include <math.h>
#include <exec/libraries.h>
#include <libraries/commodities.h>
#include <dos/dos.h>
#include <devices/inputevent.h>

#include <intuition/intuitionbase.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <proto/commodities.h>
#include <proto/graphics.h>

#include <clib/alib_protos.h>
#include <clib/extras_protos.h>
#include <debug.h>

void OpenUI(void);
void CloseUI(void);

int CXCRBK(void) {return(0);}
int chkabort(void) {return(0);}

void ProcessMsg(void);
void __asm __saveds CxFunction(register __a0 CxMsg *cxm, register __a1 CxObj *co);

/* CX stuff */
struct MsgPort *Broker_mp;
CxObj *Broker, *cocustom, *cosig;

struct Screen *MyScreen;
struct Window *Window;

#define NAME "Twitchy Mouse"

char vstring[]="$VER: Twitchy_Mouse 1.0 "__AMIGADATE__;//                                              (1.0.1)

struct NewBroker newBroker=
{
  NB_VERSION,
  NAME,
  NAME,
  "Fixes Wobbling Pointers",
  NBU_UNIQUE | NBU_NOTIFY,
  COF_SHOW_HIDE,
  0,0,0
};

struct InputEvent nev;

BOOL mine=FALSE;

struct Task *task;
ULONG cxsigflag, sig, cxobjsig;

struct Library *CxBase, *IconBase, *GTLayoutBase;

struct Libs MyLibs[]=
{
  (APTR)&IconBase,      "icon.library",               39, 0,
  (APTR)&IntuitionBase, "intuition.library",          39, 0,
  (APTR)&CxBase,        "commodities.library",        39, 0,
  (APTR)&GfxBase,       "graphics.library",           39, 0,
  0,0,0
};

struct Task *Me;
BYTE   TSig;
ULONG  TSignal;

void main (int argc, char **argv)
{
  UBYTE **ttypes;
  CxMsg *msg;

  Me=FindTask(0);

  TSig=AllocSignal(-1);
  TSignal=1<<TSig;


  nev.ie_NextEvent=0;
  nev.ie_Class=IECLASS_POINTERPOS;
  
  if(ex_OpenLibs(argc, (STRPTR)NAME , 0,0,0,MyLibs))  // from extras.lib
  {
    if(Broker_mp=CreateMsgPort())
    {
      newBroker.nb_Port=Broker_mp;
      cxsigflag=1<<Broker_mp->mp_SigBit;
      
      ttypes=ArgArrayInit(argc,argv);
      
      newBroker.nb_Pri=(BYTE)ArgInt(ttypes,(STRPTR)"CX_PRIORITY",10);
      if(Broker=CxBroker(&newBroker,NULL))
      {
        if(cocustom=CxCustom(CxFunction,0))
        {
          AttachCxObj(Broker,cocustom);
          
          if((sig=(ULONG)AllocSignal(-1)) != -1)
          {
            cxobjsig=1<<sig;
            cxsigflag|=cxobjsig;
            
            task=FindTask(NULL);
            if(cosig=CxSignal(task,sig))
            {
              AttachCxObj(cocustom,cosig);
              ActivateCxObj(Broker,1);
              ProcessMsg();
              ActivateCxObj(Broker,0);
            }
            FreeSignal(sig);
          }
        }
        DeleteCxObjAll(Broker);
        
        while(msg=(CxMsg *)GetMsg(Broker_mp))
          ReplyMsg((struct Message *)msg);
      }
      DeletePort(Broker_mp);
    }
    ArgArrayDone();
    ex_CloseLibs(MyLibs);
  }              
}

UBYTE Color=0;

void ProcessMsg(void)
{
  CxMsg *msg;
  ULONG sigrcvd,msgid,waitsigs;
  LONG returnvalue=1;

  nev.ie_Class=255;
  
  waitsigs=SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | cxsigflag | TSignal;
  
  while(returnvalue)
  {
    sigrcvd=Wait(waitsigs);

    if(TSignal & sigrcvd)
    {
      if(Window)
      {
        SetAPen(Window->RPort, Color);
        Color++;
        RectFill(Window->RPort, Window->BorderLeft, Window->BorderTop, Window->Width-Window->BorderRight-1, Window->Height-Window->BorderBottom-1);
        Delay(1);
      }
    }

    while(msg=(CxMsg *)GetMsg(Broker_mp))
    {
      msgid=CxMsgID(msg);
      ReplyMsg((struct Message *)msg);
      
      switch(msgid)
      {
        case CXCMD_DISABLE:
          ActivateCxObj(Broker,0);
          break;
        case CXCMD_ENABLE:
          ActivateCxObj(Broker,1);
          break;
        case CXCMD_APPEAR:
          OpenUI();
          break;
        case CXCMD_DISAPPEAR:
          CloseUI();
          break;
        case CXCMD_UNIQUE:
        case CXCMD_KILL:
          returnvalue=0;
          break;
      }
    }
    if(sigrcvd & SIGBREAKF_CTRL_C) returnvalue=0;
  }
  CloseUI();
}

void OpenUI(void)
{
  if(Window) return;
  
  if(MyScreen=LockPubScreen(0))
  {
    if(Window=OpenWindowTags(0,
                      WA_DragBar,        1,
                      WA_Title,          "Twitch-o-Sensor",
                      WA_InnerWidth,     100,
                      WA_InnerHeight,    10,
                      WA_DepthGadget,    1,
                      WA_SizeGadget,     1,
                      WA_MaxWidth,       16000,
                      WA_MaxHeight,       16000,
                      WA_CloseGadget,    1,
                      TAG_DONE))
    {
      return;      
    }
    else
    {
      UnlockPubScreen(0,MyScreen);
      MyScreen=0;
    }
  }
}

void CloseUI(void)
{
  if(MyScreen)
  {
    if(Window)
    {
      CloseWindow(Window);
      Window=0;
    }
    UnlockPubScreen(0,MyScreen);
    MyScreen=0;
  }
}
/*
void __asm __saveds CxFunction(register __a0 CxMsg *cxm, register __a1 CxObj *co)
{
  struct InputEvent *ie;
  static LONG ox=0,oy=0;
  LONG x,y;
  float fx,fy;
  static float px,py; 
      
  ie=(struct InputEvent*)CxMsgData(cxm);
  
  if(ie->ie_Class==255)
  {
    ie->ie_Class=IECLASS_RAWMOUSE;
  }
  else
  {
    if(ie->ie_Class==IECLASS_RAWMOUSE)
    {
      x=ie->ie_X;
      y=ie->ie_Y;
          
      fx=(x+ox)/2+px;
      fy=(y+oy)/2+py;

      ox=x;
      oy=y;

      x=fx;
      y=fy;
      
      px=fx-x;
      py=fy-y;

      ie->ie_X=x;
      ie->ie_Y=y;
 
      if((x==0 && y==0) && (ie->ie_Qualifier & 0x7fff)==0)
      {  
        if(Window && ox!=x && oy!=y)
        {
          Signal(Me, TSignal);
        }
            
        DisposeCxMsg(cxm);
      }
    }
  }
}

*/

void __asm __saveds CxFunction(register __a0 CxMsg *cxm, register __a1 CxObj *co)
{
  struct InputEvent *ie;
  static LONG ox=0,oy=0,oqual=0;
  LONG x,y,xx,yy,nqual;
      
  ie=(struct InputEvent*)CxMsgData(cxm);
  
  if(ie->ie_Class==255)
  {
    ie->ie_Class=IECLASS_RAWMOUSE;
  }
  else
  {
    if(ie->ie_Class==IECLASS_RAWMOUSE)
    {
      nqual=ie->ie_Qualifier & 0x7fff; // get rid of RawMouse bit
      xx=x=ie->ie_X;
      yy=y=ie->ie_Y;

      if(x==-ox) x=0;
      if(y==-oy) y=0;

//if(Window)
{
//      kprintf("ie.x=%ld  ie.Y=%ld  ox=%ld  oy=%ld  new ie.x=%ld  ie.y=%ld  Qual=%lx\n", xx, yy, ox, oy, x, y, ie->ie_Qualifier); 
}     
      // if new values aren't = 0 then set to new value
      if(xx!=0)  ox=xx;
      if(yy!=0)  oy=yy;
      
      ie->ie_X=x;
      ie->ie_Y=y;
      
      if((x==0 && y==0) && nqual==oqual)
      {
        if(Window)
        {
//          kprintf("  ie deleted\n");
          
          Signal(Me, TSignal);
        }
        DisposeCxMsg(cxm);
      }
      oqual=nqual;
    }
  }
}



