/** Ombre.c **/

#include "ds.h"
#include <tools/debug.h>

#define printf  kprintf

UBYTE   *wtitle   = (UBYTE *) "  Ombre -- ver 2, rev 0  ";
struct  Window      *getNewWind();

struct RastPort   *rport2 = NULL;
struct Window   *window = NULL;

struct   Task   *mytask;
UBYTE         mysig = 0;

struct  IntuitionBase   *IntuitionBase;
struct  GfxBase         *GfxBase;
struct   LayersBase      *LayersBase;

ULONG   flg = ACTIVATE | WINDOWCLOSE | NOCAREREFRESH | WINDOWDRAG
       | WINDOWDEPTH | SIMPLE_REFRESH;

ULONG   iflg = MOUSEMOVE | CLOSEWINDOW | GADGETDOWN | GADGETUP;

#define FORLAYERS(l, linfo)   for (l = (linfo)->top_layer; \
    l && !(l->Flags & LAYERBACKDROP);l=l->back)

#define BACKTOFRONT(l)    for (l = window->WLayer->LayerInfo->top_layer;   \
l && l->back; l = l->back) ; \
for (; l; l = l->front)

#define PREDLAYERS(pred, l) for (pred = l->front; pred; pred = pred->front)

#define MYPRI          7L   /* un piccolo vantaggio          */
#define INPUTPRI_PLUS   25L   /* spero di trovare ("input.device") */

USHORT   hdrop = 7;
USHORT   vdrop = 3;

main()
{
    struct  IntuiMessage    *msg;

    /* vecchi dati da *msg  */
    ULONG   class;
    UBYTE   code;
   struct Gadget *gaddress;

    struct Screen   *wbscreen;
   struct BitMap   *save_bitmap= NULL;   /* Originale RasInfo WB bitmap   */
   struct BitMap   *ribitmap   = NULL;
    struct BitMap     *bmap2      = NULL;

   int      i;
    int      it_is_done = 0;
    WORD   exitval = 0;
   ULONG   sigmask;
   ULONG   sigtaken;

   extern struct PropInfo twxtinfo;
   USHORT   twxtpot;

    if (!(IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library", 0L)))
    {
      D( printf("NO INTUITION LIBRARY\n") );
      exitval = 1;
      goto EXITING;
    }

    if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L)))
    {
      D( printf("NO GRAPHICS LIBRARY\n") );
      exitval = 2;
      goto EXITING;
    }

    if (!(LayersBase=(struct LayersBase *) OpenLibrary("layers.library", 0L)))
    {
      D( printf("NO LAYERS LIBRARY\n") );
      exitval = 2;
      goto EXITING;
    }

    /* una finestra sul workbench   */
    window = getNewWind(120, 20, 400, 30, flg, iflg);
    if (window == NULL)
    {
      D( printf("test: can't get window.\n") );
      exitval = 1;
      goto EXITING;
    }

    /* Aggiunge il bitplane al Workbench */

    wbscreen = window->WScreen;      /* lo trova   */

   /* una nuova 3-deep bitmap rimpiazza quella del RInfo del WB */
    if (!(ribitmap = (struct BitMap *)
      AllocMem((LONG) sizeof(struct BitMap), (LONG) MEMF_PUBLIC|MEMF_CLEAR)))
    {
      D( printf("alloc bitmap failed\n") );
      goto EXITING;
    }
    InitBitMap(ribitmap, 3L, (LONG) wbscreen->Width, (LONG) wbscreen->Height);

    /* colloca la bitmap per la mia rastport view del singolo bitplane   */
    if (!(bmap2 = (struct BitMap *)
      AllocMem((LONG) sizeof(struct BitMap), (LONG) MEMF_PUBLIC|MEMF_CLEAR)))
    {
      D( printf("alloc bitmap failed\n") );
      goto EXITING;
    }

    InitBitMap(bmap2, 1L, (LONG) wbscreen->Width, (LONG) wbscreen->Height);

    if (!(bmap2->Planes[0] =
      (UBYTE *) AllocRaster((LONG) wbscreen->Width, (LONG) wbscreen->Height)))
    {
      D( printf("alloc raster failed\n") );
      goto EXITING;
    }

    if (!(rport2 = (struct RastPort *)
      AllocMem((LONG) sizeof (struct RastPort), (LONG) MEMF_PUBLIC)))
    {
      D( printf("alloc rastport failed\n") );
      goto EXITING;
    }
    InitRastPort(rport2);
    rport2->BitMap = bmap2;

    SetRast(rport2, 0L);

   /* setta una nuova bitmap per il RInfo   */
   save_bitmap =  wbscreen->ViewPort.RasInfo->BitMap;
   ribitmap->Planes[0] = save_bitmap->Planes[0];
   ribitmap->Planes[1] = save_bitmap->Planes[1];
   ribitmap->Planes[2] = bmap2->Planes[0];

    Forbid();

   wbscreen->ViewPort.RasInfo->BitMap = ribitmap;

    Permit();

   it_is_done = 1;

   shadowColors(wbscreen);

    MakeScreen(wbscreen);
    RethinkDisplay();

   twxtpot = twxtinfo.HorizPot;
   hdrop = twxtpot >> TWXTSHIFT;
   vdrop = twxtpot >> (TWXTSHIFT + 1);
    drawShadow();

   /* hook nei vettori delle librerie */
   mytask = FindTask(0L);
   mysig  = AllocSignal((LONG) -1);
   sigmask = ((LONG) 1 << mysig);
   sigmask |= ((LONG) 1 << window->UserPort->mp_SigBit);
   setup_hooks();

   /* un piccolo aiuto */
   SetTaskPri(mytask, MYPRI);

    FOREVER
    {
      if ((msg = (struct IntuiMessage *)GetMsg(window->UserPort)) == NULL)
      {
         sigtaken = Wait(sigmask);
          if (sigtaken & ((LONG) 1 << mysig))
         {
            drawShadow();
         }
          continue;
      }

      class   = msg->Class;
      code   = msg->Code;
      gaddress  = (struct Gadget *) msg->IAddress;
      ReplyMsg(msg);

      switch (class)
      {
      case MOUSEMOVE:
         shadowColors(wbscreen);
         break;

      case GADGETDOWN:
         SetTaskPri(mytask, INPUTPRI_PLUS);
         break;

      case GADGETUP:
         SetTaskPri(mytask, MYPRI);

         switch (gaddress->GadgetID)
         {
         case TWIXTID:
            twxtpot = twxtinfo.HorizPot;
            hdrop = twxtpot >> TWXTSHIFT;
            vdrop = twxtpot >> (TWXTSHIFT + 1);
            drawShadow();
            break;
         case DARKID:
            shadowColors(wbscreen);
            break;
         }
         break;

      case CLOSEWINDOW:
         SetTaskPri(mytask, 0L);
         if (cleanup_hooks()) goto EXITING;
      default:
          D( printf("unknown event: class %lx\n", class) );
      }
    }

EXITING:
    if (it_is_done)
    {
      Forbid();
      wbscreen->ViewPort.RasInfo->BitMap = save_bitmap;
      Permit();
      MakeScreen(wbscreen);
      RethinkDisplay();
    }

   if (mysig) FreeSignal((LONG) mysig);

    if (rport2) FreeMem(rport2, (LONG) sizeof (struct RastPort));
    if (bmap2) 
    {
      if (bmap2->Planes[0])
      {
          FreeRaster(bmap2->Planes[0],
             (LONG) wbscreen->Width, (LONG) wbscreen->Height);
      }
      FreeMem(bmap2, (LONG) sizeof (struct BitMap));
    }
   if (ribitmap) FreeMem(ribitmap, (LONG) sizeof (struct BitMap));

    if (window) CloseWindow(window);
    if (GfxBase) CloseLibrary(GfxBase);
    if (LayersBase) CloseLibrary(LayersBase);
    if (IntuitionBase) CloseLibrary(IntuitionBase);

    exit (exitval);
}

shadowCaster(l)
register struct Layer *l;
{
   extern struct PropInfo darkinfo;
   register struct Window      *w;
   register struct RastPort   *brp;

   if (l && (w = (struct Window *) l->Window))
   {
      if (brp = w->BorderRPort)
      {
         /* GZZ   */
         return (brp->Layer == l);
      }
      else
      {
         /* non-GZZ   */
         return (l == w->WLayer);
      }
   }
   return (0);
}

drawShadowSignal(sysret)
LONG   sysret;
{
   Signal( mytask, ((LONG) 1 << mysig));
   return (sysret);
}

drawShadow(sysret)
LONG   sysret;
{
   struct Region   *shadow;

   if ( !(shadow = NewRegion()) ) return (sysret);

   bigShadow(shadow);

   SetRast(rport2, 0L);

   Forbid();
   WaitBOVP(&window->WScreen->ViewPort);

   SetAPen(rport2, 1L);
   fillRegion(rport2, shadow);
   Permit();

   DisposeRegion(shadow);
}

/* vuoi velocizzarlo? */
bigShadow(rgn)
struct   Region   *rgn;
{
   register struct Layer   *pred;
   register LONG         tmprgn;
   register struct Layer   *l;

   int            twixt;
   int            htwixt;
   int            vtwixt;
   struct Rectangle lrect;
   struct Rectangle predrect;

   if ( !(tmprgn = (LONG) NewRegion()) ) return;

   LockLayerInfo(window->WLayer->LayerInfo);

   BACKTOFRONT(l)
   {

    lrect = l->bounds;

#if 0
      notRectRegion(rgn, &lrect);
#else
      ClearRectRegion(rgn, &lrect);
#endif

#if OLDTWIXT
      twixt = 0;
#else
      htwixt = hdrop << 1;
      vtwixt = vdrop << 1;
#endif
      ClearRegion(tmprgn);


      PREDLAYERS(pred, l)
      {

         if (!shadowCaster(pred)) 
         {
            continue;
         }

         predrect = pred->bounds;

#if OLDTWIXT
         translateRect(&predrect,
            HDROP + twixt * (HDROP >> 1), VDROP + twixt * (VDROP >> 1));

         ++twixt;
#else
         translateRect(&predrect, htwixt, vtwixt);
         htwixt += hdrop;
         vtwixt += vdrop;

#endif
         OrRectRegion(tmprgn, &predrect);
      }

      AndRectRegion(tmprgn, &lrect);

      OrRegionRegion(tmprgn, rgn);
   }

   UnlockLayerInfo(window->WLayer->LayerInfo);
   DisposeRegion(tmprgn);
}

#if 0
notRectRegion(rgn, rect)
struct Region      *rgn;
struct Rectangle   *rect;
{
   OrRectRegion(rgn, rect);
   XorRectRegion(rgn, rect);
}
#endif

#define GOODRECT(r)   (((r)->MinX < (r)->MaxX) && ((r)->MinY < (r)->MaxY))

fillRegion(rp, rgn)
struct RastPort   *rp;
struct Region   *rgn;
{
   register struct   RegionRectangle   *rgnrect;
   register struct   Rectangle   *bds;

   register int      minx;
   register int      miny;

   minx = rgn->bounds.MinX;
   miny = rgn->bounds.MinY;
   rgnrect = rgn->RegionRectangle;

   while (rgnrect)
   {
      bds = &rgnrect->bounds;
      RectFill(rp,
         (LONG)minx + bds->MinX,
         (LONG) miny + bds->MinY, 
         (LONG) minx + bds->MaxX,
         (LONG) miny + bds->MaxY);

      rgnrect = rgnrect->Next;
   }
}

translateRect(r, dx, dy)
register struct   Rectangle *r;
register int      dx;
register int      dy;
{
   r->MinX   += dx;
   r->MaxX   += dx;
   r->MinY   += dy;
   r->MaxY   += dy;
}

screenRect(s, r)
register struct Screen      *s;
register struct Rectangle   *r;
{
   r->MaxX = (r->MinX = s->LeftEdge) + s->Width - 1;
   r->MaxY = (r->MinY = s->TopEdge) + s->Height - 1;
}

windowRect(w, r)
register struct Window      *w;
register struct Rectangle   *r;
{
   r->MaxX = (r->MinX = w->LeftEdge) + w->Width - 1;
   r->MaxY = (r->MinY = w->TopEdge) + w->Height - 1;
}

struct  Window * getNewWind(left, top, width, height, flg, iflg)
SHORT   left, top, width, height;
ULONG   flg, iflg;
{
   extern struct Gadget darkness;

    struct  Window  *OpenWindow();
    struct  NewWindow   nw;

    nw.LeftEdge =   (SHORT) left;
    nw.TopEdge  =   (SHORT) top;
    nw.Width    =   (SHORT) width;
    nw.Height   =   (SHORT) height;
    nw.DetailPen    =   (UBYTE) -1;
    nw.BlockPen =   (UBYTE) -1;
    nw.IDCMPFlags   =   (ULONG) iflg;

    nw.Flags    =   (ULONG) flg;

    nw.FirstGadget  =   (struct Gadget *)   &darkness;
    nw.CheckMark    =   (struct Image *)    NULL;
    nw.Title    =   (UBYTE *)   wtitle;
    nw.Screen   =   (struct Screen *)   NULL;
    nw.BitMap   =   (struct BitMap *)   NULL;
    nw.MinWidth =   (SHORT) 50;
    nw.MinHeight=   (SHORT) 30;
    /* lavoro attorno al bug  */
    nw.MaxWidth =   (SHORT) nw.Width;
    nw.MaxHeight    =   (SHORT) nw.Height;
    nw.Type     =   (USHORT) WBENCHSCREEN;

    return ((struct Window *) OpenWindow(&nw));
}


#define DARKER(c)  (((long) (((c) & 0xF) * (darkpot)) ) >> 16)

shadowColors(s)
struct Screen *s;
{
   register int   i;
   register ULONG   rgb;
   ULONG   red, green, blue;
   struct   ColorMap   *cm;
   USHORT   darkpot;

   darkpot = darkinfo.HorizPot;

   cm = s->ViewPort.ColorMap;

   for (i = 0; i < 4; ++i)
   {
      /* tre colori   */
      rgb      = GetRGB4(cm, (LONG) i);

#if   0
      rgb = ~rgb;
#endif

      blue   = DARKER(rgb);
      green   = DARKER(rgb >>= 4);
      red      = DARKER(rgb >> 4);
       SetRGB4(&s->ViewPort, (LONG) i + 4,(LONG) red,(LONG) green,(LONG) blue);
   }
}
