/*--------------------------------------------------------------------------*
                __                 __
               / /                / /__      $RCSfile: clock.c,v $
       ______ / /  ______ ______ / // /      $Revision: 1.8 $
   \/ / ____// /  / __  // ____// _  /       $Date: 93/02/18 19:40:18 $
     / /___ / /_ / /_/ // /___ / /| |        $Author: tf $
    /_____//___//_____//_____//_/ |_|        $State: Exp $

           (c) Copyright 1993 Tobias Ferber, All Rights Reserved.

 *--------------------------------------------------------------------------*/

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <time.h>
#include <stdio.h>
#include "timer.h"

#define NUMDIGITS  8    /* HH:MM:SS */
#define DHEIGHT   14
#define DWIDTH    16
#define DDIST      5    /* gap between digits */

/*#define DEBUG*/
/*#define AMIGA36*/
/*#define CTIME*/

static char rcs_id[] = "$VER: $Id: clock.c,v 1.8 93/02/18 19:40:18 tf Exp $";

extern int do_vclock(); /* area.o */
extern SHORT *digits[10], *colon, *nomp;  /* digits.S => chip memory */
static struct IntuitionBase *IntuitionBase= (struct IntuitionBase *)NULL;
static struct Image lcd[8];
BOOL fromWB= FALSE;

/* init_screenclock() is where we init our lcd[] array */

void init_screenclock(void)
{ int i;
  for(i=0;i<NUMDIGITS;i++)
  { lcd[i].LeftEdge  = i*(DWIDTH+DDIST);
    lcd[i].TopEdge   = 0;
    lcd[i].Width     = DWIDTH;
    lcd[i].Height    = DHEIGHT;
    lcd[i].Depth     = 2;
    lcd[i].ImageData = (i==2||i==5) ? colon : digits[0];
    lcd[i].PlanePick = 3;
    lcd[i].PlaneOnOff= 0;
    lcd[i].NextImage = (i==(NUMDIGITS-1)) ? (struct Image *)NULL : &lcd[i+1];
  }
}

/* display the current time (hh:mm:ss) in w's RastPort */

void set_screenclock(struct Window *w, int h, int m, int s)
{
  static int _h=0,_m=0,_s=0;

  if(h!=_h || m!=_m || s!=_s)
  { lcd[0].ImageData= digits[h/10];
    lcd[1].ImageData= digits[h%10];
    lcd[3].ImageData= digits[m/10];
    lcd[4].ImageData= digits[m%10];
    lcd[6].ImageData= digits[s/10];
    lcd[7].ImageData= digits[s%10];

    DrawImage(w->RPort, &lcd[0], (w->Width  - NUMDIGITS*(DWIDTH+DDIST))/2,
                                 (w->Height - DHEIGHT)/2 );
    _h=h; _m=m; _s=s;
  }
}

#ifdef CTIME
#include <libraries/diskfont.h>
#include <graphics/text.h>

ULONG *DiskfontBase= (ULONG *)NULL;

void setdiskfont(struct RastPort *rp, char *fname, int fsize)
{
  if(DiskfontBase= (ULONG *)OpenLibrary("diskfont.library",0L))
  { struct TextFont *tf;
    struct TextAttr ta;
    ta.ta_Name= fname;
    ta.ta_YSize= fsize;
    ta.ta_Style= FS_NORMAL;
    ta.ta_Flags= FPF_DISKFONT;
    if(tf= (struct TextFont *)OpenDiskFont(&ta))
      SetFont(rp,tf);
    CloseLibrary(DiskfontBase);
  }
}

void render_ctime(struct Window *w, long t)
{ char *s= ctime(&t);
  int l= 24; /* strlen(s) */
  SetAPen(w->RPort,3L);
  Move(w->RPort, (w->Width  - TextLength(w->RPort,s,l))/2,
                 3*(w->Height - w->RPort->TxHeight)/4 );
  Text(w->RPort, s,l);
}
#endif

#define REDSCALE    0   /* const for do_screenclock() */
#define GREENSCALE  4
#define BLUESCALE   8
#define GREYSCALE  12
#define LCDSCALE   16

static short color_table[] = { 0x000,0x500,0x500,0xF00,   /* red table */
                               0x000,0x050,0x050,0x0F0,   /* green table */
                               0x029,0x555,0x555,0xFFF,   /* blue table */
                               0x000,0x555,0x555,0xFFF,   /* grey table */
                               0xCBA,0xBA9,0xBA9,0x543,   /* lcd table */
                               0x029,0x029,0x029,0xFF0,   /* blue+yellow */
                             };


static struct NewScreen ns= { 0,0, 640,256, 2, 0,0,
                              HIRES,CUSTOMSCREEN|SCREENQUIET,
                              NULL,NULL,NULL,NULL
                            };
            
static struct NewWindow nw= { 0,0, 0,0, -1,-1,
                              MOUSEBUTTONS|RAWKEY|INTUITICKS|MOUSEMOVE,
                              BACKDROP|BORDERLESS|RMBTRAP|REPORTMOUSE|ACTIVATE,
                              NULL,NULL,NULL,NULL,NULL,
                              -1,-1,-1,-1,CUSTOMSCREEN
                            };

/*
 * Here we do the screen clock stuff.  ct should be the first index
 * of one of the color_table[] ranges (e.g. REDSCALE)
 * do_screenlock() needs the global IntuitionBase ptr to open the screen
 * and show/update the clock until a MOUSEBUTTONS event comes up.
 */

void do_screenclock(int ct)
{
  IntuitionBase= (struct IntuitionBase *)OpenLibrary("intuition.library",0L);
  if(IntuitionBase)
  { struct timerequest *tr= (struct timerequest *)NULL;

#ifdef AMIGA36  /* couldn't get this through DICE */
#include <graphics/displayinfo.h>
    if( ((struct Library *)IntuitionBase)->lib_Version >= 36L)
    { struct GfxBase *GfxBase= (struct GfxBase *)OpenLibrary("graphics.library",0L);
      if(GfxBase)
      { struct Screen *lps= (struct Screen *)LockPubScreen("Workbench");
        ULONG modeID= GetVPModeID(&lps->ViewPort);
        if(modeID!=NULL && modeID!=INVALID_ID)
          ns.ViewModes= (UWORD)modeID;   /* problem? */
        else ns.ViewModes= lps->ViewPort.Modes;

        ns.Width= lps->Width;
        ns.Height= lps->Height;

        UnLockPubScreen(NULL,lps);
        CloseLibrary(GfxBase);
      }
    }
    else
#endif

    { struct Screen ws;  /* Workbench Screen data */
      if(GetScreenData(&ws,sizeof(struct Screen),WBENCHSCREEN,NULL))
      { ns.Width= ws.Width;
        ns.Height= ws.Height;
        ns.ViewModes= ws.ViewPort.Modes;
      }
    }
    if((tr= open_timer(NULL,0L)) != (struct timerequest *)NULL)
    { struct MsgPort *tp= tr->tr_node.io_Message.mn_ReplyPort;
      queue_timer(tr,1,0);
      struct Screen *scr= (struct Screen *)NULL;
      if(scr= (struct Screen *)OpenScreen(&ns))
      { struct Window *win= (struct Window *)NULL;
        nw.Screen= scr;
        nw.Width= ns.Width;
        nw.Height= ns.Height;
        LoadRGB4(&scr->ViewPort,&color_table[4*(ct%6)],1L<<(ns.Depth));
        if(win= (struct Window *)OpenWindow(&nw))
        { struct IntuiMessage *imsg;
          BOOL done= FALSE;
#ifdef CTIME
          setdiskfont(win->RPort,"times.font",24);
#endif
          init_screenclock();
          SetPointer(win,nomp,1,1,0,0);
          while(!done)
          { ULONG class;
            long timersigmask = (1L << tp->mp_SigBit),
                 usersigmask  = (1L << win->UserPort->mp_SigBit),
                 breaksigmask = SIGBREAKF_CTRL_C,

                 sig= Wait(timersigmask | usersigmask | breaksigmask);

            done |= (sig & breaksigmask);

            if(sig & timersigmask)
            { while(GetMsg(tp) != (struct Message *)NULL)
                ;
              if(CheckIO((struct IORequest *)&tr->tr_node))
              { long t;
                struct tm *lt;
                time(&t);
                lt= (struct tm *)localtime(&t);
                set_screenclock(win,lt->tm_hour,lt->tm_min,lt->tm_sec);
#ifdef CTIME
                render_ctime(win,t);
#endif
                queue_timer(tr,1,0);
              }
            }
            if(sig & usersigmask)
            { while(imsg= (struct IntuiMessage *)GetMsg(win->UserPort))
              { class= imsg->Class;
                ReplyMsg((struct Message *)imsg);
                done |= (class==MOUSEBUTTONS ||
                         class==RAWKEY       ||
                         class==MOUSEMOVE);
              }
            }
          }
          ClearPointer(win);
          CloseWindow(win);
        }
        CloseScreen(scr);
      }
      purge_timer(tr);
      close_timer(tr);
    }
    CloseLibrary(IntuitionBase);
  }
}

static char *howtouse[]= {
 "X",          "/N", "-x", "opening position; left edge (default 0)",
 "Y",          "/N", "-y", "                  top edge (default 0)",
 "WIDTH",      "/N", "-w", "initial window size; width (default 350)",
 "HEIGHT",     "/N", "-h", "                     height (default 55)",
 "SETPEN",     "/N", "-s", "pen used for set segments (default 2)",
 "UNSETPEN",   "/N", "-u", "         for unset segments (default 3)",
 "OUTLINE",    "/N", "-o", "         for segment's outline (default 0)",
 "BACKFILL",   "/N", "-b", "         to fill the background (default 3)",
 "BLANK",      "/N", "-t", "open screen and show clock w/ given colour table",
 NULL, NULL, NULL, NULL
};

main(int argc, char *argv[])
{
  BOOL badopt= FALSE;  /* bad command line option? */
  short x=0,y=0,w=350,h=55,s=2,u=3,o=0,b=3,t=-1; /* defaults */

#ifdef DEBUG
  FILE *fp= fopen("RAM:options","w");
  if(fp)
  { int i=0;
    fprintf(fp,"argc= %d\n",argc);
    for(i=0;i<argc;i++)
      fprintf(fp,"argv[%d]= \"%s\"\n",i,argv[i]);
    fclose(fp);
  }
#endif

  if(argc>1)
  { --argc;
    ++argv;

    while(argc>0 && !badopt)
    { char *arg= argv[0];
      if(isalpha(*arg))
      { char **aopt= &howtouse[0];
        while(*aopt && stricmp(arg,*aopt))
          aopt= &aopt[4];
        if(*aopt) arg= aopt[2];
      }
      if(*arg=='-')
      { arg++;
        switch(*arg)
        {
/* -x */  case 'x': case 'X':
            if(arg[1]) x= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) x= (short)atoi(argv[0]);
              else { puts("Error: missing x-position value after keyword");
                     badopt=TRUE;
                   }
            }
            if(x<0)
            { printf("Illegal left edge value %d\n",x);
              badopt= TRUE;
            }
            break;
/* -y */  case 'y': case 'Y':
            if(arg[1]) y= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) y= (short)atoi(argv[0]);
              else { puts("Error: missing y-position value after keyword");
                     badopt=TRUE;
                   }
            }
            if(y<0)
            { printf("Illegal top edge value %d\n",y);
              badopt= TRUE;
            }
            break;
/* -w */  case 'w': case 'W':
            if(arg[1]) w= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) w= (short)atoi(argv[0]);
              else { puts("Error: missing width value after keyword");
                     badopt=TRUE;
                   }
            }
            if(w<0)
            { printf("Illegal WIDTH value %d\n",w);
              badopt= TRUE;
            }
            break;
/* -h */  case 'h': case 'H':
            if(arg[1]) h= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) h= (short)atoi(argv[0]);
              else { puts("Error: missing height value after keyword");
                     badopt=TRUE;
                   }
            }
            if(h<0)
            { printf("Illegal HEIGTH value %d\n",h);
              badopt= TRUE;
            }
            break;
/* -s */  case 's': case 'S':
            if(arg[1]) s= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) s= (short)atoi(argv[0]);
              else { puts("Error: missing set pen value after keyword");
                     badopt=TRUE;
                   }
            }
            if(s<0 || s>255)
            { printf("Illegal SET pen %d -- should be in [0..255]\n",s);
              badopt= TRUE;
            }
            break;
/* -u */  case 'u': case 'U':
            if(arg[1]) u= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) u= (short)atoi(argv[0]);
              else { puts("Error: missing unset pen value after keyword");
                     badopt=TRUE;
                   }
            }
            if(u<0 || u>255)
            { printf("Illegal UNSET pen %d -- should be in [0..255]\n",u);
              badopt= TRUE;
            }
            break;
/* -o */  case 'o': case 'O':
            if(arg[1]) o= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) o= (short)atoi(argv[0]);
              else { puts("Error: missing outline pen value after keyword");
                     badopt=TRUE;
                   }
            }
            if(o<0 || o>255)
            { printf("Illegal OUTLINE pen %d -- should be in [0..255]\n",o);
              badopt= TRUE;
            }
            break;
/* -b */  case 'b': case 'B':
            if(arg[1]) b= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) b= (short)atoi(argv[0]);
              else { puts("Error: missing backfill value after keyword");
                     badopt=TRUE;
                   }
            }
            if(b<0 || b>255)
            { printf("Illegal BACKFILL pen %d -- should be in [0..255]\n",b);
              badopt= TRUE;
            }
            break;
/* -t */  case 't': case 'T':
            if(arg[1]) t= (short)atoi(&arg[1]);
            else
            { argv++;
              argc--;
              if(argc>0) t= (short)atoi(argv[0]);
              else t=0;
            }
            break;
          case '?':
            puts(&rcs_id[6]);
            puts("Written by Tobias Ferber for the PUBLIC DOMAIN");
            puts("Refer to vClock.DOC for options.  ? for AmigaDOS help");
            badopt= TRUE;
            break;
/* ?? */  default:
            printf("bad option -%c.\n",*arg);
            badopt= TRUE;
            break;
        }
      }
      else if(*arg=='?')
      { char **usage= &howtouse[0];
        while(*usage)
        { printf("%s%s,",usage[0],usage[1]);
          usage= &usage[4];
        }
        printf("\b:\n\n");
        usage= &howtouse[0];
        while(*usage)
        { printf("%-10s or '%s'  %s\n",usage[0],usage[2],usage[3]);
          usage= &usage[4];
        }
        exit(0);
      }
      else
      { printf("Unknown keyword %s\n",arg);
        badopt= TRUE;
      }
      --argc;
      ++argv;
    }
  }
  if(!badopt)
  { if(t>=0) do_screenclock(t);
    else if(do_vclock(x,y,w,h,s,u,o,b))
      puts("vClock failed.");
  }
  if(!fromWB) exit(badopt?20:0);
}

#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <workbench/icon.h>

struct IconBase *IconBase;

wbmain(struct WBStartup *WBStartup)
{
  int ac= 0;      /* argument counter */
  char *av[10*2]; /* argument value: 10 keywords, 10 values */
  fromWB= TRUE;

  IconBase= (struct IconBase *)OpenLibrary(ICONNAME,LIBRARY_VERSION);
  if(IconBase)
  { struct WBArg *wbarg= WBStartup->sm_ArgList;
    struct DiskObject *icon;
    av[ac++]= (char *)wbarg->wa_Name;
    CurrentDir(wbarg->wa_Lock);
    icon= GetDiskObject(wbarg->wa_Name);
    if(icon)
    { char *ttv, **tt= icon->do_ToolTypes,
                 **usage= &howtouse[0];

      while(*usage)
      { ttv= FindToolType(tt,*usage);
        if(ttv)
        { av[ac++]= *usage;
          av[ac++]= ttv;
        }
        usage= &usage[4];
      }
      (void)main(ac,av);
      FreeDiskObject(icon);
    }
    /* else (we got no icon) -> we were not started from Workbench */
    CloseLibrary(IconBase);
  }
  else
  { av[ac++]= "[icon.library is hiding]";
    (void)main(ac,av);
  }
}
