/*
    Keeping to tradition, the Khaled Mardam-Bey philosophy, this program
    is completely undocumented so as to give you prime experience in
    learning how to debug source code.

      The program also contains many redundant bits and pieces so as to
    make your learning experience that much more profoundly educational
    and exceptional.

    This is how NOT to write a program.

    Got it ? Okay, great, now YOU can start teaching others by coding in
    exactly the same way.

    There's something very deep in that last sentence, can't quite
    pinpoint it though, hmmm...

    compile with  cc Msizer.c
                  ln Msizer.o -lc

    Warning - It won't bite unless you try to compile it.
              Linking is okay though... <grin>

   This source is totally PD.

   written by Khaled Mardam-Bey, 14th October 1989.
*/

#include <exec/lists.h>
#include <exec/interrupts.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/execbase.h>
#include <exec/types.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <exec/exec.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <devices/timer.h>
#include <devices/trackdisk.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <ctype.h>
#include <stdio.h>
#include <functions.h>
#include <graphics/gfxbase.h>

  /* Key Definitions */
#define QKEY         0x45
#define CONTROL      0x63
#define THRESHX      16
#define THRESHY      8
#define MINX         40
#define MINY         24

  /* input events */
#define QUIT         0x1
#define FINISHUP     0x2
#define MOVEIT       0x4
#define DOWINDOW     0x8

#define DELAY    1000000L

char PortName[] = "PePeTe";
char WrittenBy[] = "Khaled Mardam-Bey 1989";

SHORT i,j,k,l;
SHORT nomore = 0, NoGo = 0;
SHORT corner = 0;
SHORT dx,dy,ff,xx,yy,oo;
SHORT DoIT = 0, there = 0;
SHORT LastX,LastY;
SHORT x,y,ox,oy,fx,fy;
SHORT event;
SHORT lastcode = 0;
long  sec = 0,micro = 0;
SHORT rbuttonisdown = 0, lbuttonisdown = 0;
SHORT ww, wh, te, le, sw, sh;

struct Library *OpenLibrary();
struct ViewPort *ViewPortAddress();

struct MsgPort       *inputPort = NULL;
struct IOStdReq      *inputReq = NULL;
struct MsgPort       *TimerPort = NULL;
struct Screen        *s, *Screen = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase       *GfxBase = NULL;
struct Window        *window;
struct RastPort      *rp;
struct Layer_Info    *li;
struct LayersBase    *LayersBase = NULL;

void move(x,y) SHORT x,y; { Move(rp, (long) x, (long) y); }
void draw(x,y) SHORT x,y; { Draw(rp, (long) x, (long) y); }
void plot(x,y) SHORT x,y; { WritePixel(rp, (long) x, (long) y); }

struct timerequest Timer_Req;
long   TimerSig,tdevice = 1;

struct InputEvent phoney;
struct HotInfo
  {
  struct Task *hotTask;
  long  hotSig;
  } hotStuff;

long signum = -1;

struct Interrupt handlerStuff;
struct defPort
{
  struct MsgPort mp;
};

struct defPort *defPortPtr;
char defPortName[] = "WiNnYpO";
SHORT updating = 0;

void HandlerInterface()
{
#asm
  movem.l a4,-(sp)
  jsr _geta4#
  movem.l   A0/A1,-(sp)
  jsr       _myhandler
  addq.l    #8,A7
  movem.l (sp)+,a4
#endasm
}

struct InputEvent *myhandler(ev1, hotStuff)
struct InputEvent *ev1;
struct HotInfo *hotStuff;
{
  struct InputEvent *ev, *last;
  SHORT removeit;
  SHORT evcode,evqual;

  event = 0;
  for (ev=ev1,last = NULL; ev; ev=ev->ie_NextEvent)
  {
    evcode = ev->ie_Code;
    evqual = ev->ie_Qualifier;
    removeit = 0;

    if ((ev->ie_Class != IECLASS_TIMER))
    {
      if (ev->ie_Class == IECLASS_RAWKEY)
      {
        if ((evcode >= 0x80) && (evcode == (lastcode | 0x80))) {
          DoIT = 0;
          event |= FINISHUP;
          removeit = 1;
          lastcode = 0; }
        else {
          lastcode = 0;
          removeit = 0;
        }

       if (evcode == CONTROL)
       {
         lastcode = evcode;
         removeit = 1;
         DoIT = 1;
       }
       else
       if (evcode == QKEY) {
         if (DoIT) DoIT = 2;
         lastcode = evcode;
         removeit = 1;
       }
       else DoIT = 0;
      }
    }

    if (ev->ie_Class == IECLASS_RAWMOUSE)
    {
      if (evcode == (IECODE_LBUTTON | 0x80)) {
        lbuttonisdown = 0;
        if (DoIT) event |= DOWINDOW;
        else event |= FINISHUP;
      }
      else {
        if (evcode == IECODE_LBUTTON) {
          if (DoIT == 2) event |= QUIT;
          if ((lbuttonisdown == 0) && (DoIT == 1)) removeit = 1;
          lbuttonisdown = 1;
        }

        if ((lbuttonisdown) && (DoIT)) {
          event |= MOVEIT;
        }
      }
    }

    if (removeit)
      if (last == NULL)
        ev1 = ev->ie_NextEvent;
      else
        last->ie_NextEvent = ev->ie_NextEvent;
    else
      last = ev;
  }

  if (event)
    Signal(hotStuff->hotTask,hotStuff->hotSig);
  return(ev1);
}

main()
{
  struct IntuiMessage *Msg;
  long                class;

  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L);
  if (!IntuitionBase) exit(0L);

  GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
  if (!GfxBase) exit(0L);

  LayersBase = (struct LayersBase *) OpenLibrary("layers.library",0L);
  if (!LayersBase) exit(0L);

  defPortPtr = (struct defPort *) FindPort(defPortName);
  if (defPortPtr == NULL)
  {
    if ((defPortPtr = (struct defPort *) AllocMem((long)sizeof(struct defPort),MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
      exit(10);
  }
  else
    updating = 1;

  if (updating)
    Uninstall();

  defPortPtr->mp.mp_Node.ln_Pri = 0;
  defPortPtr->mp.mp_Node.ln_Type = NT_MSGPORT;
  NewList(&(defPortPtr->mp.mp_MsgList));
  defPortPtr->mp.mp_Node.ln_Name = (char *) &(defPortName);

  AddPort(defPortPtr);

  if((signum = AllocSignal((long)-1)) == -1)
    Uninstall();

  hotStuff.hotSig = 1 << signum;
  hotStuff.hotTask = FindTask(NULL);

  if(!(inputPort = CreatePort(PortName,0)))
    Uninstall();
  if(!(inputReq = CreateStdIO(inputPort)))
    Uninstall();

  handlerStuff.is_Data = (APTR)&hotStuff;
  handlerStuff.is_Code = HandlerInterface;
  handlerStuff.is_Node.ln_Pri = 55;
  handlerStuff.is_Node.ln_Name = "PoOpHaNy";

  if(OpenDevice("input.device",0L,inputReq,0L) != 0)
    Uninstall();

  inputReq->io_Command = IND_ADDHANDLER;
  inputReq->io_Data = (APTR)&handlerStuff;

  DoIO(inputReq);

  if ((TimerPort = CreatePort("TimPodle", 0L)) == NULL)
    Uninstall();

  if ((tdevice = OpenDevice(TIMERNAME, UNIT_VBLANK, &Timer_Req, 0L)) != 0)
    Uninstall();
  Timer_Req.tr_node.io_Message.mn_ReplyPort = TimerPort;
  Timer_Req.tr_node.io_Command = TR_ADDREQUEST;
  Timer_Req.tr_node.io_Flags = 0;
  Timer_Req.tr_node.io_Error = 0;

  TimerSig = (1L << TimerPort->mp_SigBit);

  QueTimer();

  (void)SetTaskPri(FindTask(NULL), 20L);

  for (;;)
  {
    Wait(hotStuff.hotSig | TimerSig);

    if (Msg = (struct IntuiMessage *)GetMsg(TimerPort))
      if (Msg) QueTimer();

    if (breakcheck()) Uninstall();
    breakreset();

    if (event & QUIT) Uninstall();

    if (event & FINISHUP) {
      FiniUpi();
      corner = 0;
      nomore = 0;
    }

    if (event & DOWINDOW) MoveThatWindow();

    if (event & MOVEIT) {
      if (nomore == 0) CheckPP();
      else if (corner != 0) ElBorders();
    }
  event = 0;
  }
}

FiniUpi()
{
  if (there) {
    xx = LastX + dx; yy = LastY + dy;
    DoEm();
    UnlockLayers(li);
    there = 0;
  }
}

QueTimer()
{
  Timer_Req.tr_time.tv_secs = 0;
  Timer_Req.tr_time.tv_micro = DELAY;
  SendIO(&Timer_Req.tr_node);
}

ElBorders()
{
  if ((LastX != s->MouseX) || (LastY != s->MouseY)) {
    xx = LastX + dx;
    yy = LastY + dy;
    DoEm();
    xx = s->MouseX + dx;
    yy = s->MouseY + dy;
    DoEm();
  }

  there = 1;
  LastX = s->MouseX;
  LastY = s->MouseY;
}

DoEm()
{
  if (corner == 1) do1();
  else if (corner == 2) do2();
  else if (corner == 3) do3();
  else if (corner == 4) do4();
}

do1()
{
  while (xx < 0) xx = xx + 1;
  while (yy < 0) yy = yy + 1;

  while ((le+ww) > s->Width) ww = ww -1;
  while ((te+wh) > s->Height) wh = wh - 1;

  if (xx > (le+ww-MINX)) xx = (le+ww-MINX);
  if (yy > (te+wh-MINY)) yy = (te+wh-MINY);

  move( (SHORT) xx, (SHORT) yy);
  draw( (SHORT) (le+ww-1), (SHORT) yy);
  draw( (SHORT) (le+ww-1), (SHORT) (te+wh-1));
  draw( (SHORT) xx, (SHORT) (te+wh-1));
  draw( (SHORT) xx, (SHORT) yy);
}

do2()
{
  while (xx > s->Width) xx = xx - 1;
  while (xx < 0) xx = xx + 1;
  while (yy < 0) yy = yy + 1;
  while (yy > s->Height) yy = yy - 1;

  while ((te+wh) > s->Height) wh = wh - 1;

  if (xx < (le+MINX)) xx = le+MINX;
  if (yy > (te+wh-MINY)) yy = (te+wh-MINY);

  move( (SHORT) xx-1, (SHORT) yy);
  draw( (SHORT) le, (SHORT) yy);
  draw( (SHORT) le, (SHORT) (te+wh-1));
  draw( (SHORT) xx-1, (SHORT) (te+wh-1));
  draw( (SHORT) xx-1, (SHORT) yy);
}

do3()
{
  while (xx < 0) xx = xx + 1;
  while (xx > s->Width) xx = xx - 1;
  while (yy < 0) yy = yy + 1;
  while (yy > s->Height) yy = yy - 1;

  while ((le+ww) > s->Width) ww = ww -1;

  if (xx > (le+ww-MINX)) xx = (le+ww-MINX);
  if (yy < (te+MINY)) yy = (te+MINY);

  move( (SHORT) xx, (SHORT) yy-1);
  draw( (SHORT) (le+ww-1), (SHORT) yy-1);
  draw( (SHORT) (le+ww-1), (SHORT) te);
  draw( (SHORT) xx, (SHORT) te);
  draw( (SHORT) xx, (SHORT) yy-1);
}

do4()
{
  while (xx < 0) xx = xx + 1;
  while (xx > s->Width) xx = xx - 1;
  while (yy < 0) yy = yy + 1;
  while (yy > s->Height) yy = yy - 1;

  if (xx < (le+MINX)) xx = (le+MINX);
  if (yy < (te+MINY)) yy = (te+MINY);

  move( (SHORT) xx-1, (SHORT) yy-1);
  draw( (SHORT) xx-1, (SHORT) te);
  draw( (SHORT) le, (SHORT) te);
  draw( (SHORT) le, (SHORT) yy-1);
  draw( (SHORT) xx-1, (SHORT) yy-1);
}

CheckPP()
{
  window = IntuitionBase->ActiveWindow;

  if (window != NULL) {
    le = window->LeftEdge;    te = window->TopEdge;
    ww = window->Width;       wh = window->Height;

    s = window->WScreen;
    sw = s->Width;
    sh = s->Height;

    rp=&(s->RastPort);

    SetAPen(rp,1L);
    SetBPen(rp,2L);
    SetDrMd(rp, COMPLEMENT | JAM1);

    LastX = ox = x = s->MouseX;
    LastY = oy = y = s->MouseY;

    corner = 0;

    if ((y >= te) && (y < (te + THRESHY)))  {                    /* top edge */
      if ((x >= le) && (x < (le + THRESHX)))                     /* left */
      { corner = 1;
        dx = le - x;
        dy = te - y;
      }
      else
      if ((x <= (le + ww)) && (x > (le + ww - THRESHX)))         /* right */
      { corner = 2;
        dx = (le+ww) - x;
        dy = te - y;
      }
    }
    else
    if ((y <= (te + wh)) && (y > (te + wh - THRESHY)))  {        /* bottom edge */
      if ((x >= le) && (x < (le + THRESHX)))                     /* left */
      { corner = 3;
        dx = le - x;
        dy = (te+wh) - y;
      }
      else
      if ((x <= (le + ww)) && (x > (le + ww - THRESHX)))         /* right */
      { corner = 4;
        dx = (le+ww) - x;
        dy = (te+wh) - y;
      }
    }

    if (corner != 0) {
      LastX = x = s->MouseX;
      LastY = y = s->MouseY;
      li = &(s->LayerInfo);
      LockLayers(li);
      ox = xx = LastX + dx;
      oy = yy = LastY + dy;
      DoEm();
      nomore = 1;
      there = 1;
    }
    else nomore = 0;
  }
}

MoveThatWindow()
{
  Forbid();
  Disable();

  FiniUpi();

  fx = xx;
  fy = yy;

  xx = fx - ox; if (xx < 0) xx = xx * -1;
  yy = fy - oy; if (yy < 0) yy = yy * -1;

  if (corner == 1) {
    if ((fx >= ox) && (fy >= oy)) {
      while (xx > (le+ww-MINX)) xx = xx - 1;
      while (yy > (te+wh-MINY)) yy = yy - 1;
      SizeWindow(window, (-1L * xx), (-1L * yy));
      MoveWindow(window, (1L * xx), (1L * yy));
    }
    else
    if ((fx <= ox) && (fy <= oy)) {
      while ((le-xx) < 0) xx = xx - 1;
      while ((te-yy) < 0) yy = yy - 1;
      MoveWindow(window, (-1L * xx), (-1L * yy));
      SizeWindow(window, (1L * xx), (1L * yy));
    }
    else
    if ((fx >= ox) && (fy <= oy)) {
      while (xx > (le+ww-MINX)) xx = xx - 1;
      while ((te-yy) < 0) yy = yy - 1;
      SizeWindow(window, (-1L * xx), 0L);
      MoveWindow(window, (1L * xx), (-1L * yy));
      SizeWindow(window, 0L, (1L * yy));
    }
    else
    if ((fx <= ox) && (fy >= oy)) {
      while (yy > (te+wh-MINY)) yy = yy - 1;
      while ((le-xx) < 0) xx = xx - 1;
      SizeWindow(window, 0L, (-1L * yy));
      MoveWindow(window, (-1L * xx), (1L * yy));
      SizeWindow(window, (1L * xx), 0L);
    }
  }
  else
  if (corner == 2) {
    if ((fx >= ox) && (fy >= oy)) {
      while (yy > (te+wh-MINY)) yy = yy - 1;
      while ((xx+le+ww) > s->Width) xx = xx - 1;
      SizeWindow(window, (1L * xx), (-1L * yy));
      MoveWindow(window, 0L, (1L * yy));
    }
    else
    if ((fx <= ox) && (fy <= oy)) {
      while ((te-yy) < 0) yy = yy - 1;
      MoveWindow(window, 0L , (-1L * yy));
      SizeWindow(window, (-1L * xx), (1L * yy));
    }
    else
    if ((fx >= ox) && (fy <= oy)) {
      while ((le+ww+xx) > s->Width) xx = xx - 1;
      while ((te-yy) < 0) yy = yy - 1;
      MoveWindow(window, 0L, (-1L * yy));
      SizeWindow(window, (1L * xx), (1L * yy));
    }
    else
    if ((fx <= ox) && (fy >= oy)) {
      while (yy > (te+wh-MINY)) yy = yy - 1;
      SizeWindow(window, (-1L * xx), (-1L * yy));
      MoveWindow(window, 0L, (1L * yy));
    }
  }
  else
  if (corner == 3) {
    if ((fx >= ox) && (fy >= oy)) {
      while (xx > (le+ww-MINX)) xx = xx - 1;
      while ((te+yy+wh) > s->Height) yy = yy - 1;
      SizeWindow(window, (-1L * xx), (1L * yy));
      MoveWindow(window, (1L * xx), 0L);
    }
    else
    if ((fx <= ox) && (fy <= oy)) {
      while ((le-xx) < 0) xx = xx - 1;
      while ((te+wh-yy) < MINY) yy = yy - 1;
      MoveWindow(window, (-1L * xx), 0L);
      SizeWindow(window, (1L * xx), (-1L * yy));
    }
    else
    if ((fx >= ox) && (fy <= oy)) {
      while ((te+wh-yy) < MINY) yy = yy - 1;
      while (xx > (le+ww-MINX)) xx = xx - 1;
      SizeWindow(window, (-1L * xx), (-1L * yy));
      MoveWindow(window, (1L * xx), 0L);
    }
    else
    if ((fx <= ox) && (fy >= oy)) {
      while ((le-xx) < 0) xx = xx - 1;
      while ((te+yy+wh) > s->Height) yy = yy - 1;
      MoveWindow(window, (-1L * xx), 0L);
      SizeWindow(window, (1L * xx), (1L * yy));
    }
  }
  else
  if (corner == 4) {
    if ((fx >= ox) && (fy >= oy)) {
      while ((te+yy+wh) > s->Height) yy = yy - 1;
      while ((xx+le+ww) > s->Width) xx = xx - 1;
      SizeWindow(window, (1L * xx), (1L * yy));
    }
    else
    if ((fx <= ox) && (fy <= oy)) {
      while (xx > (le+ww-MINX)) xx = xx - 1;
      while ((te+wh-yy) < MINY) yy = yy - 1;
      SizeWindow(window, (-1L * xx), (-1L * yy));
    }
    else
    if ((fx >= ox) && (fy <= oy)) {
      while ((le+ww+xx) > s->Width) xx = xx - 1;
      while ((te+wh-yy) < MINY) yy = yy - 1;
      SizeWindow(window, (1L * xx), (-1L * yy));
    }
    else
    if ((fx <= ox) && (fy >= oy)) {
      while (xx > (le+ww-MINX)) xx = xx - 1;
      while ((te+yy+wh) > s->Height) yy = yy - 1;
      SizeWindow(window, (-1L * xx), (1L * yy));
    }
  }
  Enable();
  Permit();
  nomore = corner = 0;
}

breakcheck()
{
   if (SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
      return (1);
   else
      return (0);
}
 
breakreset()
{
   SetSignal(0L, SIGBREAKF_CTRL_C);
}

Chk_Abort()
{
  return(0);
}

Uninstall()
{
  if (!updating)
    {
    if (inputReq)
      {
      inputReq->io_Command = IND_REMHANDLER;
      inputReq->io_Data = (APTR)&handlerStuff;
      DoIO(inputReq);

      CloseDevice(inputReq);
      DeleteStdIO(inputReq);
      }

    if (inputPort)    DeletePort(inputPort);
    if (signum > -1)  FreeSignal(signum);

    if (tdevice == 0)
      {
      AbortIO(&Timer_Req.tr_node);
      CloseDevice(&Timer_Req);
      }

    if (TimerPort)
      DeletePort(TimerPort);

    if (defPortPtr)
      {
      if (defPortPtr->mp.mp_Node.ln_Name)
        RemPort(defPortPtr);
      FreeMem(defPortPtr,(long)sizeof(struct defPort));
      }
    }

  if (LayersBase)
    CloseLibrary(LayersBase);
  if (GfxBase)
    CloseLibrary(GfxBase);
  if (IntuitionBase)
    CloseLibrary(IntuitionBase);
  exit(0L);
}
