/* $VER: $Id: area.c,v 1.8 93/02/18 19:40:54 tf Exp $ © 1992,93 by Tobias Ferber */

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuition.h>
#include <time.h>
#include "timer.h"

/* globals = */
static struct GfxBase *GfxBase;
static struct IntuitionBase *IntuitionBase;
static struct Window *win;
static struct AreaInfo areainfo;
static struct TmpRas tras;
static APTR areabuf= (APTR)NULL;
static PLANEPTR trasbuf= (PLANEPTR)NULL;

/* the following vd_xxxx are all set by rethink_dimensions() */
static short vd_width,   /* current vector digit dimensions */
             vd_height,
             vd_ypos,
             vd_spcx;

#define MAXVECTORS (64*6)  /* for the area stuff */
#define AREASIZE ((MAXVECTORS+1) * 5) /* +1 vector for AreaEnd() */
#define TRASSIZE ((win->WScreen->Width/8) * win->WScreen->Height)

static struct NewWindow nw= {
  0,0,
  350,55,
  -1,-1,
  CLOSEWINDOW|NEWSIZE,
  WINDOWCLOSE|WINDOWDEPTH|WINDOWSIZING|WINDOWDRAG|RMBTRAP|GIMMEZEROZERO|SMART_REFRESH,
  NULL,NULL,
  (UBYTE*)"vClock",
  NULL,NULL,
  180,40,
  -1,-1,
  WBENCHSCREEN
};

#define EDGES 8

struct ptxy { short x,y; } a,b,c,d,e,f,g, /* segment offset */
                           top[EDGES],
                           bottom[EDGES],
                           left[EDGES],
                           right[EDGES],
                           mid[4];

/* Init the vector tables of all segments of a vector digit with
 * width w and height h.  This function will be called when using
 * rethink_dimensions().
 */

void init_vectors(short w, short h)
{ unsigned short dx1= 5*w/8,
                 dx2= w/2,
                 dx3= w/8,
                 dx4= w/16,
                 dy1= 3*h/8,
                 dy2= h/4,
                 dy3= h/8,
                 dy4= h/16;

  /* corrections */

  dx1= dx4+dx2+dx4;
  dy1= dy4+dy2+dy4;

/*
  printf("dx1=%d, dx2=%d, dx3=%d, dx4=%d, "
         "dy1=%d, dy2=%d, dy3=%d, dy4=%d\n",dx1,dx2,dx3,dx4,
                                            dy1,dy2,dy3,dy4 );
*/

  /* segment offsets */

  a.x= dx3+dx4;        a.y= 0;
  b.x= dx3+2*dx4+dx1;  b.y= dy4;
  c.x= dx3+2*dx4+dx1;  c.y= 2*dy4+dy1;
  d.x= dx3+2*dx4;      d.y= 2*dy1+2*dy4;
  e.x= 0;              e.y= 2*dy4+dy1;
  f.x= 0;              f.y= dy4;
  g.x= dx3+2*dx4;      g.y= dy4+dy1;

  /* delta values */

  top[0].x= dx1;       top[0].y= 0;
  top[1].x= 0;         top[1].y= dy4;
  top[2].x= -dx4;      top[2].y= 0;
  top[3].x= 0;         top[3].y= dy4;
  top[4].x= -dx2;      top[4].y= 0;
  top[5].x= 0;         top[5].y= -dy4;
  top[6].x= -dx4;      top[6].y= 0;
  top[7].x= 0;         top[7].y= -dy4;

  right[0].x= dx3;     right[0].y= 0;
  right[1].x= 0;       right[1].y= dy1;
  right[2].x= -dx3;    right[2].y= 0;
  right[3].x= 0;       right[3].y= -dy4;
  right[4].x= -dx4;    right[4].y= 0;
  right[5].x= 0;       right[5].y= -dy2;
  right[6].x= dx4;     right[6].y= 0;
  right[7].x= 0;       right[7].y= -dy4;

  left[0].x= dx3;      left[0].y= 0;
  left[1].x= 0;        left[1].y= dy4;
  left[2].x= dx4;      left[2].y= 0;
  left[3].x= 0;        left[3].y= dy2;
  left[4].x= -dx4;     left[4].y= 0;
  left[5].x= 0;        left[5].y= dy4;
  left[6].x= -dx3;     left[6].y= 0;
  left[7].x= 0;        left[7].y= -dy1;

  bottom[0].x= dx2;    bottom[0].y= 0;
  bottom[1].x= 0;      bottom[1].y= dy4;
  bottom[2].x= dx4;    bottom[2].y= 0;
  bottom[3].x= 0;      bottom[3].y= dy4;
  bottom[4].x= -dx1;   bottom[4].y= 0;
  bottom[5].x= 0;      bottom[5].y= -dy4;
  bottom[6].x= dx4;    bottom[6].y= 0;
  bottom[7].x= 0;      bottom[7].y= -dy4;

  mid[0].x= dx2;       mid[0].y= 0;
  mid[1].x= 0;         mid[1].y= dy3;
  mid[2].x= -dx2;      mid[2].y= 0;
  mid[3].x= 0;         mid[3].y= -dy3;
}

/* rethink the vector digit dimensions for the hh:mm:ss display
 * in a rastport of width w and height h and call init_vectors()
 */

void rethink_dimensions(short w, short h)
{
  vd_ypos   = win->Height/40;
  vd_spcx   = win->Width /30;
  vd_height = win->Height - win->BorderTop
                          - win->BorderBottom
                          - vd_ypos;
  vd_width  = (win->Width - win->BorderLeft
                          - win->BorderRight
                          - 9 * vd_spcx )/6;

  init_vectors(vd_width, vd_height);
/*
  printf("vd_ypos=%d, vd_spcx=%d, vd_height=%d, vd_width=%d\n",
    vd_ypos,vd_spcx,vd_height,vd_width);
*/
}

/* Missing in the graphics.library ? */

void AreaPolyDraw(struct RastPort *rp, short x,
                                       short y,
                                       short edges,
                                       short dxy[])
{ short e;
  AreaMove(rp,x,y);
  for(e=0;e<edges;e++)
  { x+= dxy[2*e];
    y+= dxy[2*e+1];
    AreaDraw(rp,x,y);
  }
}

/* Render a vector digit at (xpos|ypos) into the given RastPort rp.
 * Note that this RastPort *MUST* have a fully initialized TmpRas
 * and AreaInfo structure.
 * If you pass a 'smart' value >=0 to this function, it only
 * changes the differing segments between `digit' and `smart'.
 * SET segments will be filled with rp's FgPen, UNSET segments
 * using the BgPen.
 * Passed digit and smart values *MUST* be in [0..15], except
 * smart which can be -1 for non-smart rendering.
 */

void render_vecdigit(struct RastPort *rp,short xpos,
                                         short ypos,
                                         int digit,
                                         int smart)
{ static const UBYTE seglist[17][8]= {
  /* a b c d e f g */
  { 1,1,1,1,1,1,0 },    /* 0                      */
  { 0,1,1,0,0,0,0 },    /* 1      aaaaaaaaaa      */
  { 1,1,0,1,1,0,1 },    /* 2   ff  aaaaaaaa  bb   */
  { 1,1,1,1,0,0,1 },    /* 3   fff          bbb   */
  { 0,1,1,0,0,1,1 },    /* 4   fff          bbb   */
  { 1,0,1,1,0,1,1 },    /* 5   fff          bbb   */
  { 1,0,1,1,1,1,1 },    /* 6   ff            bb   */
  { 1,1,1,0,0,0,0 },    /* 7       gggggggg       */
  { 1,1,1,1,1,1,1 },    /* 8   ee  gggggggg  cc   */
  { 1,1,1,1,0,1,1 },    /* 9   eee          ccc   */
  { 1,1,1,0,1,1,1 },    /* A   eee          ccc   */
  { 0,0,1,1,1,1,1 },    /* B   eee          ccc   */
  { 1,0,0,1,1,1,0 },    /* C   ee  dddddddd  cc   */
  { 0,1,1,1,1,0,1 },    /* D      dddddddddd      */
  { 1,0,0,1,1,1,1 },    /* E                      */
  { 1,0,0,0,1,1,1 } };  /* F                      */

  if(smart<0)
  { if(seglist[digit][0]) AreaPolyDraw(rp,xpos+a.x,ypos+a.y,EDGES,top);
    if(seglist[digit][1]) AreaPolyDraw(rp,xpos+b.x,ypos+b.y,EDGES,right);
    if(seglist[digit][2]) AreaPolyDraw(rp,xpos+c.x,ypos+c.y,EDGES,right);
    if(seglist[digit][3]) AreaPolyDraw(rp,xpos+d.x,ypos+d.y,EDGES,bottom);
    if(seglist[digit][4]) AreaPolyDraw(rp,xpos+e.x,ypos+e.y,EDGES,left);
    if(seglist[digit][5]) AreaPolyDraw(rp,xpos+f.x,ypos+f.y,EDGES,left);
    if(seglist[digit][6]) AreaPolyDraw(rp,xpos+g.x,ypos+g.y,4,mid);
    AreaEnd(rp);
  }
  else
  { BYTE apen= rp->FgPen;
    if(seglist[digit][0] &! seglist[smart][0])
      AreaPolyDraw(rp,xpos+a.x,ypos+a.y,EDGES,top);
    if(seglist[digit][1] &! seglist[smart][1])
      AreaPolyDraw(rp,xpos+b.x,ypos+b.y,EDGES,right);
    if(seglist[digit][2] &! seglist[smart][2])
      AreaPolyDraw(rp,xpos+c.x,ypos+c.y,EDGES,right);
    if(seglist[digit][3] &! seglist[smart][3])
      AreaPolyDraw(rp,xpos+d.x,ypos+d.y,EDGES,bottom);
    if(seglist[digit][4] &! seglist[smart][4])
      AreaPolyDraw(rp,xpos+e.x,ypos+e.y,EDGES,left);
    if(seglist[digit][5] &! seglist[smart][5])
      AreaPolyDraw(rp,xpos+f.x,ypos+f.y,EDGES,left);
    if(seglist[digit][6] &! seglist[smart][6])
      AreaPolyDraw(rp,xpos+g.x,ypos+g.y,4,mid);
    AreaEnd(rp);

    SetAPen(rp,rp->BgPen);
    if(seglist[smart][0] &! seglist[digit][0])
      AreaPolyDraw(rp,xpos+a.x,ypos+a.y,EDGES,top);
    if(seglist[smart][1] &! seglist[digit][1])
      AreaPolyDraw(rp,xpos+b.x,ypos+b.y,EDGES,right);
    if(seglist[smart][2] &! seglist[digit][2])
      AreaPolyDraw(rp,xpos+c.x,ypos+c.y,EDGES,right);
    if(seglist[smart][3] &! seglist[digit][3])
      AreaPolyDraw(rp,xpos+d.x,ypos+d.y,EDGES,bottom);
    if(seglist[smart][4] &! seglist[digit][4])
      AreaPolyDraw(rp,xpos+e.x,ypos+e.y,EDGES,left);
    if(seglist[smart][5] &! seglist[digit][5])
      AreaPolyDraw(rp,xpos+f.x,ypos+f.y,EDGES,left);
    if(seglist[smart][6] &! seglist[digit][6])
      AreaPolyDraw(rp,xpos+g.x,ypos+g.y,4,mid);
    AreaEnd(rp);
    SetAPen(rp,apen);
  }
}

void set_vclock(struct RastPort *rp, int h, int m, int s, int clr)
{
  static int _hh= -1, _h= -1,
             _mm= -1, _m= -1,
             _ss= -1, _s= -1;

  int hh= h/10,
      mm= m/10,
      ss= s/10;

  h%=10;
  m%=10;
  s%=10;

  if(clr) _hh= _h= _mm= _m= _ss= _s= -1;

  if(_hh!=hh)
  { render_vecdigit(rp,vd_spcx,vd_ypos,hh,_hh);
    _hh=hh;
  }
  if(_h!=h)
  { render_vecdigit(rp,vd_width+2*vd_spcx,vd_ypos,h,_h);
    _h=h;
  }
  if(_mm!=mm)
  { render_vecdigit(rp,2*vd_width+4*vd_spcx,vd_ypos,mm,_mm);
    _mm=mm;
  }
  if(_m!=m)
  { render_vecdigit(rp,3*vd_width+5*vd_spcx,vd_ypos,m,_m);
    _m=m;
  }
  if(_ss!=ss)
  { render_vecdigit(rp,4*vd_width+7*vd_spcx,vd_ypos,ss,_ss);
    _ss=ss;
  }
  if(_s!=s)
  { render_vecdigit(rp,5*vd_width+8*vd_spcx,vd_ypos,s,_s);
    _s=s;
  }
}

/* open the system stuff and allocate needed memory */

int open_vectorstuff(void)
{ int ok=0;
  if(IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L))
  { struct Screen s; /* for the Workbench Screen data */
    if(GetScreenData(&s,sizeof(struct Screen),WBENCHSCREEN,NULL))
    { nw.Width+=  s.WBorLeft + s.WBorRight;
      nw.Height+= s.WBorTop  + s.WBorBottom;
      if(nw.LeftEdge+nw.Width > s.Width)
      { if(nw.Width > s.Width) nw.Width= s.Width;
        nw.LeftEdge= s.Width-nw.Width;
      }
      if(nw.TopEdge+nw.Height > s.Height)
      { if(nw.Height > s.Height) nw.Height= s.Height;
        nw.TopEdge= s.Height-nw.Height;
      }
    }
    if(GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L))
    { if(win=(struct Window *)OpenWindow(&nw))
      { if(areabuf= (APTR)AllocMem(AREASIZE,MEMF_PUBLIC|MEMF_CLEAR))
        { if(trasbuf= (PLANEPTR)AllocRaster(win->WScreen->Width,
                                            win->WScreen->Height))
          { InitArea(&areainfo,areabuf,MAXVECTORS);
            win->RPort->AreaInfo=&areainfo;
            win->RPort->TmpRas=(struct TmpRas *)
              InitTmpRas(&tras,trasbuf,TRASSIZE);
            ok=1;
          }
          else free(areabuf);
        }
        else CloseWindow(win);
      }
      else CloseLibrary(GfxBase);
    }
    else CloseLibrary(IntuitionBase);
  }
  return ok;
}

void close_vectorstuff(void)
{ if(trasbuf) FreeRaster(trasbuf,win->WScreen->Width,
                                 win->WScreen->Height);
  if(areabuf) FreeMem(areabuf,AREASIZE);
  if(win) CloseWindow(win);
  if(GfxBase) CloseLibrary(GfxBase);
  if(IntuitionBase) CloseLibrary(IntuitionBase);
}

update_titles(struct Window *w, struct tm *lt)
{ static const char *dn[]= { "Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun" },
                    *mn[]= { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
                             "Sep","Oct","Nov","Dec" };

  static int d= -1, m= -1, y= -1;
  static char wtitle[20];

  if(d != lt->tm_mday || m != lt->tm_mon || y != lt->tm_year)
  { d= lt->tm_mday;
    m= lt->tm_mon;
    y= lt->tm_year;
    sprintf(wtitle,"%s %02d-%s-%02d",dn[lt->tm_wday],d,mn[m],y);
    SetWindowTitles(w,wtitle,-1L); /* -1 = old title */
  }
}

int do_vclock(short leftedge,
               short topedge,
               short width,
               short height,
               short setpen,
               short unsetpen,
               short outline,
               short backfill)

{ int rc= -1; /* return code; != 0 indicates failure */

  nw.LeftEdge= leftedge;
  nw.TopEdge= topedge;
  if(width > nw.MinWidth) nw.Width= width;
  if(height > nw.MinHeight) nw.Height= height;

  if( open_vectorstuff() )
  { struct timerequest *tr= open_timer(NULL,0L);
    if(tr != (struct timerequest *)NULL)
    { struct MsgPort *tp= tr->tr_node.io_Message.mn_ReplyPort;
      struct RastPort *rp= win->RPort;
      BOOL done= FALSE;

      rc= 0;
      queue_timer(tr,1,0);
      rethink_dimensions(win->Width,win->Height);
      SetAPen(rp,setpen);
      SetBPen(rp,unsetpen);
      SetOPen(rp,outline);
      SetRast(rp,backfill);
      /*BNDRYOFF(rp);*/
      set_vclock(rp,88,88,88,-1); /* all segments high to init bndry */

      while(!done)
      {
        long timersigmask = (1L << tp->mp_SigBit),
             usersigmask  = (1L << win->UserPort->mp_SigBit),

             sig = Wait(timersigmask | usersigmask);

        if(sig & timersigmask)
        { while(GetMsg(tp) != (struct Message *)NULL)
            ;
          if(CheckIO((struct IORequest *)tr))
          { long t;
            struct tm *lt;
            time(&t);
            lt= (struct tm *)localtime(&t);
            set_vclock(rp,lt->tm_hour,lt->tm_min,lt->tm_sec,0);
            update_titles(win,lt);
            queue_timer(tr,1,0);
          }
        }
        if(sig & usersigmask)
        { ULONG class;
          struct IntuiMessage *imsg;
          while(imsg= (struct IntuiMessage *)GetMsg(win->UserPort))
          { done |= ((class= imsg->Class) == CLOSEWINDOW);
            ReplyMsg((struct Message *)imsg);
            if(class==NEWSIZE)
            { rethink_dimensions(win->Width,win->Height);
              SetRast(rp,backfill);
              set_vclock(rp,88,88,88,-1);
            }
          }
        }
      }
      purge_timer(tr);
      close_timer(tr);
    }
    close_vectorstuff();
  }
  return rc;
}
