// Lupe V1.0 - June 1995 - © Frank Toepper
// a simple lens
// AmigaOS 3.0

#include <intuition/intuition.h>
#include <intuition/imageclass.h>
#include <intuition/icclass.h>
#include <intuition/gadgetclass.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <graphics/scale.h>
#include <devices/inputevent.h>
#include <libraries/commodities.h>
#include <libraries/gadtools.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxmacros.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#include <clib/commodities_protos.h>
#include <clib/gadtools_protos.h>
#include <wbstartup.h>

#pragma header

#define MAXSCALEFAC 20

ULONG innerwidth = 200, innerheight = 100, scalefac = MAXSCALEFAC / 2;
ULONG cxsigflag, scrdepth;
LONG waitmask = 0;
UWORD leftoff, topoff, bottomoff, rightoff;
WORD sizeiw, sizeih, winx, winy, winleft = 0, wintop;
BYTE timesig;
APTR VisualInfo;
struct MsgPort *broker_mp, *userport;
struct Task *thistask;
struct Window *mywin = NULL;
struct Screen *scr = NULL;
struct Menu *menu;
struct MenuItem *item;
struct BitMap *srcbm = NULL, *destbm = NULL, *scrbm;
BOOL hires, newlook, jump = FALSE, mm = TRUE;
CxObj *broker, *customcxobj;
Object *propgadget;
char pubscreenname[MAXPUBSCREENNAME];
struct Library *CxBase, *GfxBase, *GadToolsBase;

extern struct ExecBase *SysBase;

BOOL jumpFunc ();
BOOL mmFunc ();
BOOL AboutFunc ();
BOOL QuitFunc ();

struct EasyStruct es_about = {
   20, 0,
   "Lupe",
   "%s%s%s\n\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n%s\n\n%s",
   "%s"
};

char *era_about[] = {
   "Lupe V1.0 (", __DATE__, ")",
   "written by:",
   "  Frank Toepper",
   "  Maxim Gorki Strasse 5A",
   "  Greifswald",
   "  17491",
   "  GERMANY",
   "EMail:",
   "  toepper@rz.uni-greifswald.de",
   "This program is Public Domain",
   "Ok"
};

struct NewBroker newbroker = {
   NB_VERSION,
   "Lupe",
   "Lupe V1.0",
   "A Simple Lens",
   3,
   0,
   0,
   NULL,
   0
};

struct BitScaleArgs bsa = {
   // Src Koords
   0, 0,
   innerwidth / scalefac,
   innerheight / scalefac,
   // Src Faktoren
   1, 1,
   0, 0,
   innerwidth,
   innerheight,
   // Dest Faktoren
   scalefac, scalefac,
   // Bitmaps
   NULL, NULL,
   0,
   0, 0,
   0, 0
};

int setupscreen ()
{
struct Screen *tmpscr;

   if (scr)
   {
      if (NextPubScreen (scr, pubscreenname))
      {
         if ((tmpscr = LockPubScreen (pubscreenname)) != scr)
         {
            FreeVisualInfo (VisualInfo);
            UnlockPubScreen (NULL, scr);
            scr = tmpscr;
         }
         else return 1;
      }
   }
   else if (!(scr = LockPubScreen (NULL))) return 2;
   if (!(VisualInfo = GetVisualInfoA (scr, NULL))) return 3;
   return 0L;
}

void CloseDownScreen ()
{
   if (VisualInfo)
   {
      FreeVisualInfo (VisualInfo);
      VisualInfo = NULL;
   }
   if (scr)
   {
      UnlockPubScreen (NULL, scr);
      scr = NULL;
   }
}

void getoffsets ()
{
struct DrawInfo *drawinfo;
APTR sizeobject;
ULONG attr;
   
   hires = !(scr->Flags & SCREENHIRES);
   hires ? (rightoff = 18) : (rightoff = 13);
   if (drawinfo = GetScreenDrawInfo (scr))
   {
      if (sizeobject = NewObject (NULL, SYSICLASS,
       SYSIA_Which, SIZEIMAGE,
       SYSIA_DrawInfo, drawinfo,
       SYSIA_Size, hires ? SYSISIZE_HIRES : SYSISIZE_MEDRES))
      {
         if (GetAttr (IA_Width, sizeobject, &attr))
         {
            sizeiw = attr;
            rightoff =  (UWORD) attr;
         }
         if (GetAttr (IA_Height, sizeobject, &attr))
         {
            sizeih = attr;
         }
         newlook = (drawinfo->dri_Flags & DRIF_NEWLOOK) && (drawinfo->dri_Depth != 1);
         DisposeObject (sizeobject);
      }
      FreeScreenDrawInfo (scr, drawinfo);
   }
   topoff = scr->RastPort.TxHeight + (UWORD) scr->WBorTop + 1;
   leftoff = scr->WBorLeft;
   bottomoff = scr->WBorBottom;
   scrbm = &scr->BitMap;
   scrdepth = scr->BitMap.Depth;
}

int allocbm ()
{
   if (destbm) FreeBitMap (destbm);
   if (srcbm) FreeBitMap (srcbm);
   winx = mywin->Width;
   winy = mywin->Height;
   innerwidth = winx - (leftoff + rightoff);
   innerheight = winy - (topoff + bottomoff);
   if (!(srcbm = AllocBitMap (innerwidth / scalefac, innerheight / scalefac, scrdepth, BMF_CLEAR, scrbm)))
      return 1;
   if (!(destbm = AllocBitMap (innerwidth, innerheight, scrdepth, BMF_CLEAR, scrbm)))
      return 2;
   bsa.bsa_SrcWidth = innerwidth / scalefac;
   bsa.bsa_SrcHeight = innerheight / scalefac;
   bsa.bsa_DestWidth = innerwidth;
   bsa.bsa_DestHeight = innerheight;
   bsa.bsa_SrcBitMap = srcbm;
   bsa.bsa_DestBitMap = destbm;
   bsa.bsa_XDestFactor = bsa.bsa_YDestFactor = scalefac;
   return 0;
}

BOOL openwin ()
{
WORD bw, rh;
struct Rectangle zoom = {
   60 + leftoff + rightoff,
   30 + topoff + bottomoff + sizeih,
   -1, -1
};
struct NewMenu newmenu[] = {
   { NM_TITLE, "Project",   0, 0, 0, 0, },
   {  NM_ITEM, "Jump",      "J", 0, 0, jumpFunc, },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0, },
   {  NM_ITEM, "MouseMove", "M", (mm ? CHECKED : 0) | CHECKIT, 0, mmFunc, },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0, },
   {  NM_ITEM, "About",     "A", 0, 0, AboutFunc, },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0, },
   {  NM_ITEM, "Quit",      "Q", 0, 0, QuitFunc, },
};

   if (hires) bw = rh = 2;
   else bw = rh = 1;
   if (menu = CreateMenusA (newmenu, NULL))
   {
      if (LayoutMenus (menu, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_DONE))
      {
         if (propgadget = (Object *) NewObject (NULL, PROPGCLASS,
          PGA_Freedom,     FREEVERT,
          ICA_TARGET,      ICTARGET_IDCMP,
          PGA_NewLook,     TRUE,
          PGA_Borderless,  newlook,
          PGA_Total,       MAXSCALEFAC,
          PGA_Visible,     1,
          PGA_Top,         scalefac,
          GA_RelVerify,    1,
          GA_RelRight,     bw - sizeiw + 3,
          GA_Top,          topoff + rh,
          GA_Width,        sizeiw - bw - bw - 4,
          GA_RelHeight,    -topoff - sizeih - rh - rh,
          GA_RightBorder,  TRUE,
          TAG_DONE))
         {
            if (mywin = OpenWindowTags (NULL,
             WA_Activate,       1,
             WA_CloseGadget,    1,
             WA_DepthGadget,    1,
             WA_DragBar,        1,
             WA_SizeBRight,     1,
             WA_SizeGadget,     1,
             WA_AutoAdjust,     1,
             WA_SimpleRefresh,  1,
             WA_ReportMouse,    1,
             WA_NewLookMenus,   1,
             WA_MaxHeight,      -1,
             WA_MaxWidth,       -1,
             WA_MinHeight,      30 + topoff + bottomoff,
             WA_MinWidth,       60 + leftoff + rightoff,
             WA_IDCMP,          CLOSEWINDOW | IDCMPUPDATE | NEWSIZE | MENUPICK | MOUSEMOVE,
             WA_Height,         innerheight + topoff + bottomoff,
             WA_Width,          innerwidth + leftoff + rightoff,
             WA_Left,           winleft,
             WA_Gadgets,        propgadget,
             WA_Top,            wintop,
             WA_Title,          "Lupe",
             WA_PubScreen,      scr,
             WA_Zoom,           &zoom,
             TAG_DONE))
            {
               SetMenuStrip (mywin, menu);
               userport = mywin->UserPort;
               ScreenToFront (scr);
               return TRUE;
            }
         }
      }
   }
   return FALSE;
}

void closewin ()
{
   ClearMenuStrip (mywin);
   CloseWindow (mywin);
   FreeMenus (menu);
   DisposeObject (propgadget);
   if (srcbm) FreeBitMap (srcbm);
   if (destbm) FreeBitMap (destbm);
   srcbm = NULL;
   destbm = NULL;
}

void refresh ()
{
LONG x, y;

   x = scr->MouseX - innerwidth / scalefac / 2;
   y = scr->MouseY - innerheight / scalefac / 2;
   if (x < 0) x = 0;
   if (x > (scr->Width - innerwidth / scalefac))
      x = scr->Width - innerwidth / scalefac;
   if (y < 0) y = 0;
   if (y > (scr->Height - innerheight / scalefac))
      y = scr->Height - innerheight / scalefac;
   BltBitMap (scrbm, x, y, srcbm, 0, 0, innerwidth / scalefac, innerheight / scalefac, ABNC | ABC, ~0, NULL);
   WaitBlit ();
   if (scalefac)
   {
      BitMapScale (&bsa);
      WaitBlit ();
      if ((winx == mywin->Width) && (winy == mywin->Height))
         BltBitMapRastPort (destbm, 0, 0, mywin->RPort, leftoff, topoff, innerwidth, innerheight, ABNC | ABC);
   }
   else BltBitMapRastPort (srcbm, 0, 0, mywin->RPort, leftoff, topoff, innerwidth, innerheight, ABNC | ABC);
   WaitBlit ();
}

void CxFunction (CxMsg *cxm, CxObj *co)
{
struct InputEvent *ie;

   ie = (struct InputEvent *) CxMsgData (cxm);
   if ((ie->ie_Class == IECLASS_TIMER) || ((ie->ie_Class == IECLASS_RAWMOUSE) && mm))
      Signal (thistask, 1 << timesig);
}

BOOL initbroker ()
{
   if (broker_mp = CreateMsgPort ())
   {
      newbroker.nb_Port = broker_mp;
      cxsigflag = 1L << broker_mp->mp_SigBit;
      if (broker = CxBroker (&newbroker, NULL))
      {
         if (customcxobj = CxCustom (CxFunction, 0L))
         {
            if (!CxObjError (customcxobj))
            {
               AttachCxObj (broker, customcxobj);
               ActivateCxObj (broker, 1L);
               return TRUE;
            }
         }
      }
   }
   return FALSE;
}

void processmsg ()
{
struct IntuiMessage *intuimsg = NULL, m;
CxMsg *msg;
BOOL cont = TRUE;
LONG sigmask, msgid, msgtype;;
UWORD code;
static BOOL (*func)();

   waitmask = (1 << userport->mp_SigBit) | (1 << timesig) | cxsigflag | SIGBREAKF_CTRL_C;
   while (cont)
   {
      sigmask = Wait (waitmask);
      if (sigmask & cxsigflag)
      {
         while (msg = (CxMsg *) GetMsg (broker_mp))
         {
            msgid = CxMsgID (msg);
            msgtype = CxMsgType (msg);
            ReplyMsg ((struct Message *) msg);
            switch (msgtype)
            {
             case CXM_COMMAND:
               switch (msgid)
               {
                case CXCMD_DISABLE:
                  ActivateCxObj (broker, 0L);
                  break;
                case CXCMD_ENABLE:
                  ActivateCxObj (broker, 1L);
                  break;
                case CXCMD_UNIQUE:
                case CXCMD_KILL:
                  cont = FALSE;
                  break;
               }
            }
         }
      }
      if (sigmask & (1 << userport->mp_SigBit))
      {
         jump = FALSE;
         while (intuimsg = (struct IntuiMessage *) GetMsg (userport))
         {
            CopyMem ((char *) intuimsg, (char *) &m, (long) sizeof (struct IntuiMessage));
            ReplyMsg ((struct Message *) intuimsg);
            switch (m.Class)
            {
             case CLOSEWINDOW:
               cont = FALSE;
               break;
             case NEWSIZE:
               if (allocbm ()) cont = FALSE;
               break;
             case IDCMPUPDATE:
               ULONG h;
               GetAttr (PGA_Top, propgadget, &h);
               if (++h != scalefac)
               {
                  scalefac = h;
                  if (allocbm ()) cont = FALSE;
               }
               break;
             case MENUPICK:
               code = m.Code;
               while ((code != MENUNULL) && cont && !jump)
               {
                  item = ItemAddress (menu, code);
                  func = (BOOL (*)()) GTMENUITEM_USERDATA (item);
                  cont = func ();
                  if (!jump) code = item->NextSelect;
               }
               break;
            }
         }
      }
      if (sigmask & (1 << timesig))
      {
         refresh ();
      }
      if (sigmask & SIGBREAKF_CTRL_C)
      {
         cont = FALSE;
      }
   }
}

BOOL mmFunc ()
{
   if ((mm == FALSE) && (item->Flags & CHECKED)) mm = TRUE;
   else if (mm && !(item->Flags & CHECKED)) mm = FALSE;
   return TRUE;   
}

BOOL AboutFunc ()
{
   EasyRequestArgs (NULL, &es_about, NULL, era_about);
   return TRUE;
}

BOOL jumpFunc ()
{
int returnvalue;

   if (!(returnvalue = setupscreen ()))
   {
      waitmask &= ~(1 << userport->mp_SigBit);
      wintop = mywin->TopEdge;
      winleft = mywin->LeftEdge;
      closewin ();
      getoffsets ();
      if (!openwin ()) return FALSE;
      waitmask |= 1 << userport->mp_SigBit;
      if (allocbm ()) return FALSE;
      jump = TRUE;
      return TRUE;
   }
   else if (returnvalue == 1) return TRUE;      // kein anderer Pubscreen
   return FALSE;
}

BOOL QuitFunc ()
{
   return FALSE;
}

void main ()
{
   if (CxBase = OpenLibrary ("commodities.library", 37))
   {
      if (GfxBase = OpenLibrary ("graphics.library", 39))
      {
         if (GadToolsBase = OpenLibrary ("gadtools.library", 37))
         {
            if (!setupscreen ())
            {
               getoffsets ();
               wintop = topoff;
               if (scr->Height > 300) innerheight <<= 1;
               if (openwin ())
               {
                  if (!allocbm ())
                  {
                     if ((timesig = AllocSignal (-1L)) != -1)
                     {
                        thistask = SysBase->ThisTask;
                        if (initbroker ())
                        {
                           processmsg ();
                           DeleteCxObjAll (broker);
                        }
                        FreeSignal (timesig);
                     }
                  } 
                  closewin ();
               }
               CloseDownScreen ();
            }
            CloseLibrary (GadToolsBase);
         }
         CloseLibrary (GfxBase);
      }
      CloseLibrary (CxBase);
   }
}