/* main.c */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "swis.h"

#include "bbc.h"
#include "wimp.h"
#include "wimpt.h"
#include "template.h"
#include "alarm.h"
#include "visdelay.h"
#include "res.h"
#include "resspr.h"
#include "msgs.h"
#include "version.h"
#include "werr.h"
#include "win.h"
#include "event.h"
#include "font.h"
#include "coords.h"
#include "baricon.h"
#include "dbox.h"
#include "help.h"
#include "options.h"
#include "flex.h"

#include "intalk.h"

#include "misc.h"
#include "lookup.h"

#include "tracker.h"

#define main_HELPMAX     4096
#define main_HELPMAXLINE 100
#define main_HELPLMAX    256

#define main_TFONT       "Homerton.Medium"
#define main_SFONT       "WIMPSymbol"
#define main_FONTSIZE    10
#define main_DFONT       "BitFont.Medium"
#define main_DSIZE       12

#define main_MARGIN 16

enum { menu_INFO = 1, menu_QUIT };

typedef struct main__font
{  struct main__font *downgrade;
   char              name[40];
   int               size;
} main__font;

static wimp_w panel, shadow;
static wimp_i icon;
static char main_helpbuf[main_HELPMAX];
static char main_newbuf[main_HELPMAX];
static char main_lbuf[main_HELPLMAX];
static int  main__wimpver;
static main__font *main__text, *main__symbols;

/* Formatting information for the text in main_helpbuf */

static struct
{  char *lp;
   int len;
} main__fline[main_HELPMAXLINE];
static int lused;
static int lspc, base;
static BOOL active;

#define main__iswimp(c) ((c) >= 0x80 && (c) < 0x90)

static main__font *main__declarefont(char *name, int size, main__font *downgrade)
{  main__font *fnt;

   if (fnt = malloc(sizeof(main__font)), !fnt)
      werr(TRUE, "No memory for font data");

   fnt->downgrade = downgrade;
   strcpy(fnt->name, name);
   fnt->size = size;

   return fnt;
}

static os_error *main__findfont(main__font *font, font *hp)
{  os_error *err;

   if (err = font_find(font->name, font->size * 16, font->size * 16, 0, 0, hp), !err)
      return NULL;

   if (font->downgrade) return main__findfont(font->downgrade, hp);
   return err;
}

static char *main__process(char *s, font tf, font sf)
{  int bp;
   BOOL sym = FALSE;

   bp = 0;
   main_lbuf[bp++] = 26;
   main_lbuf[bp++] = tf;

   while (*s && bp < main_HELPLMAX-4)
   {  if (main__iswimp(*s))
      {  if (!sym)
         {  main_lbuf[bp++] = 26;
            main_lbuf[bp++] = sf;
            sym = TRUE;
         }
      }
      else
      {  if (sym)
         {  main_lbuf[bp++] = 26;
            main_lbuf[bp++] = tf;
            sym = FALSE;
         }
      }
      
      main_lbuf[bp++] = *s++;
   }

   main_lbuf[bp] = '\0';

   return main_lbuf;
}

static os_error *main__open(int x, int y, int newheight)
{  wimp_wstate state;
   os_error *err;
   int rx = (bbc_vduvar(bbc_XWindLimit) + 1) << bbc_vduvar(bbc_XEigFactor);

   if (err = wimp_get_wind_state(panel, &state), err) return err;

   if (*main_helpbuf == '\0' || !active)
   {  if (state.flags & wimp_WOPEN)
      {   if ((err = wimp_close_wind(panel), err) ||
             (err = wimp_close_wind(shadow), err))
            return err;
      }
      return NULL;
   }

   state.o.behind = -1;

   /* change height? */

   if (newheight >= 0) state.o.box.y0 = state.o.box.y1 - newheight;

   misc_boxto(&state.o.box, x + 32, y - 32 - (state.o.box.y1 - state.o.box.y0));

   if (state.o.box.x1 + 16 + 16 > rx)
      misc_boxby(&state.o.box, -(state.o.box.x1 - state.o.box.x0 + 16 + 16 + 48), 0);

   if (state.o.box.y0 - 16 - 16 < 0)
      misc_boxby(&state.o.box, 0, state.o.box.y1 - state.o.box.y0 + 16 + 16);

   if (err = wimp_open_wind(&state.o), err) return err;

   misc_boxby(&state.o.box, 16, -16);

   state.o.w = shadow;
   state.o.behind = panel;

   if (err = wimp_open_wind(&state.o), err) return err;

   return NULL;
}

static os_error *main__paintbit(wimp_redrawstr *r)
{  wimp_box clip, fill;
   int fl, ll, l, eolc;
   os_error *err;
   font tf, sf;
   os_regset regs;
   struct
   {  int spx, spy;
      int ltx, lty;
      int x0, y0, x1, y1;
   } r5b;

   clip = r->g;
   coords_box_toworkarea(&clip, (coords_cvtstr *) &r->box);

   if (err = main__findfont(main__text,    &tf), err) return err;
   if (err = main__findfont(main__symbols, &sf), err) sf = (font) -1;

   fl = misc_max(-(clip.y1 + main_MARGIN) / lspc, 0);
   ll = misc_min(-(clip.y0 + main_MARGIN) / lspc, lused-1);
 
   if (err = misc_setfontcolours(tf, 0, 7), err) goto fail1;
   if (err = misc_setfontcolours(sf, 0, 7), err) goto fail1;

   /* Paint the bit before the first line */
 
   wimp_setcolour(0);
 
   fill.x0 = r->box.x0;
   fill.y0 = r->box.y1 - r->scy - main_MARGIN;
   fill.x1 = r->box.x1;
   fill.y1 = r->box.y1;
 
   misc_fillrect(&fill);

   for (l = fl; l <= ll; l++)
   {  r5b.spx = r5b.spy = 0;
      r5b.ltx = r5b.lty = 0;
      
      r5b.x0 = r->box.x0 * 400;
      r5b.y0 = (-(main_MARGIN + lspc * (l+1)) + r->box.y1 - r->scy) * 400;
      r5b.x1 = r->box.x1 * 400;
      r5b.y1 = r5b.y0 + lspc * 400;

      eolc = main__fline[l].lp[main__fline[l].len];
      main__fline[l].lp[main__fline[l].len] = '\0';

      regs.r[0] = tf;
      regs.r[1] = (int) (sf == (font) -1 ? main__fline[l].lp : main__process(main__fline[l].lp, tf, sf));
      regs.r[2] = 0x322;
      regs.r[3] = (main_MARGIN + r->box.x0 - r->scx) * 400;
      regs.r[4] = (-(main_MARGIN + lspc * (l+1)) + base + r->box.y1 - r->scy) * 400;
      regs.r[5] = (int) &r5b;
      regs.r[6] = 0;
      regs.r[7] = 0;

      if (err = os_swix(Font_Paint, &regs), err) goto fail1;

      main__fline[l].lp[main__fline[l].len] = eolc;
   }

   wimp_setcolour(0);
 
   fill.x0 = r->box.x0;
   fill.y0 = r->box.y0;
   fill.x1 = r->box.x1;
   fill.y1 = r->box.y1 - r->scy - main_MARGIN - lspc * lused;
 
   misc_fillrect(&fill);

   if (sf != (font) -1 && (err = font_lose(sf), err)) goto fail;
   if (err = font_lose(tf), err) return err;

   return NULL;

fail1:
   main__fline[l].lp[main__fline[l].len] = eolc;
   if (sf != (font) -1) font_lose(sf);
fail:
   font_lose(tf);
   return err;
}

static os_error *main__update(int x, int y, int newheight, BOOL fixed)
{  static int ox = -1000, oy = -1000;
   static BOOL actwas = -1;
   os_error *err;
   int nx, ny;

   if ((!fixed && (x != ox || y != oy)) || active != actwas || newheight >= 0)
   {  if (fixed) { nx = ox; ny = oy; } else { nx = x; ny = y; }
      if (err = main__open(nx, ny, newheight), err) return err;
      ox = nx;
      oy = ny;
      actwas = active;
   }
   return NULL;
}

static os_error *main__splitat(char *bp, int maxw, int *splitpos, font tf, font sf)
{  os_regset regs;
   os_error *err;
   BOOL iswimp;
   char *sp, *ep;
   int ec;

   struct
   {  int spx, spy;
      int ltx, lty;
      int split;
      int x0, y0, x1, y1;
   } r5b;

   maxw *= 400;

   iswimp = main__iswimp(*bp);

   if (splitpos) *splitpos = strlen(bp);

   for (sp = bp; *sp; )
   {  for (ep = sp; *ep && iswimp == main__iswimp(*ep); ep++)
         ;

      ec = *ep;
      *ep = '\0';

      regs.r[0] = (iswimp && sf != (font) -1) ? sf : tf;
      regs.r[1] = (int) sp;                                               
      regs.r[2] = 0x320;
      regs.r[3] = maxw;  /* max. width before split */
      regs.r[4] = 0x7FFFFFFF;
      regs.r[5] = (int) &r5b;
      regs.r[6] = 0;           /* not used */
      regs.r[7] = 0;
   
      r5b.spx =
      r5b.spy =
      r5b.ltx =
      r5b.lty = 0;
      r5b.split = ' ';
   
      if (err = os_swix(Font_ScanString, &regs), err) return err;

      *ep = ec;  /* restore character */

      if ((char *) regs.r[1] < ep || *ep == '\0')
      {  if (splitpos) *splitpos = (char *) regs.r[1] - bp;
         return NULL;
      }

      maxw -= regs.r[3];
      sp = ep;
      iswimp = !iswimp;
   }

   return NULL;
}

/* Format the contents of main_helpbuf */ 

static os_error *main__format()
{  char *bp, *lp, *ep;
   int eolc, lc, split, width;
   font tf, sf;
   wimp_wstate state;
   os_error *err;
   wimp_mousestr mse;

   if (err = wimp_get_wind_state(panel, &state), err) return err;

   width = state.o.box.x1 - state.o.box.x0 - main_MARGIN * 2;

   if (err = main__findfont(main__text,    &tf), err) return err;
   if (err = main__findfont(main__symbols, &sf), err) sf = (font) -1;

   bp = main_helpbuf;
   lp = bp + strlen(bp);
   lc = 0; /* line count */

   do
   {  for (ep = bp; ep < lp && *ep != '\n'; ep++) ;  /* end of buffer or '\n' */
      eolc = *ep; *ep = '\0';   /* temp. term. line */

      /* split physical line into logical lines */

      do
      {  main__fline[lc].lp = bp;  /* remember line start */
         if (err = main__splitat(bp, width, &split, tf, sf), err) goto fail2;
         main__fline[lc].len = split;

         if (++lc >= main_HELPMAXLINE)
            goto done;

         bp += split;
         while (bp < ep && *bp == ' ') bp++;

      } while (bp < ep);

      *ep = eolc;

      if (bp < lp && *bp == '\n') bp++;

   } while (bp < lp);

done: /* used when line count overflows */
   lused = lc;

   font_lose(tf);
   if (sf != -1) font_lose(sf);

   if (err = wimp_get_point_info(&mse), err) return err;
   if (err = main__update(mse.x, mse.y, main_MARGIN * 2 + lspc * lused, bbc_inkey(-1)), err) return err;
   if (err = misc_force(panel), err) return err;

   return NULL;

fail2:
   *ep = eolc;
   font_lose(tf);
   if (sf != -1) font_lose(sf);
   return err;
}

/* Perform message expansion */

static char *main__expand(char *message)
{  char *mp;
   int bp;

   for (bp = 0, mp = message; bp < main_HELPMAX - 1 && *mp; )
   {  if (*mp == '\\')
      {  mp++;
         if (*mp == '\\')
            main_newbuf[bp++] = *mp++;
         else
         {  char tokn[8], *message;
            int len;

            strcpy(tokn, "T?:?");
            tokn[1] = *mp++;
            message = lookup(tokn);

            len = misc_min(strlen(message), main_HELPMAX - 1 - bp);
            strncpy(main_newbuf + bp, message, len);
            bp += len;
         }
      }
      else if (*mp == '|')
      {  mp++;;
         if (tolower(*mp) == 'm')
         {  main_newbuf[bp++] = '\n';
            mp++;;
         }
         else
            main_newbuf[bp++] = *mp++;
      }
      else
         main_newbuf[bp++] = *mp++;
   }
   
   main_newbuf[bp] = '\0';

   return main_newbuf;
}

static os_error *main__mkwind(wimp_w *wp, char *name)
{  wimp_wind *wdef;
   os_error *err;

   if (err = misc_template(name, &wdef), err) return err;
   if (err = wimp_create_wind(wdef, wp), err) return err;
   return NULL;
}

static os_error *main__redraw(void)
{  wimp_redrawstr r;
   BOOL more;
   os_error *err, *perr;

   r.w = panel;
   if (err = wimp_redraw_wind(&r, &more), err) return err;

   perr = NULL;

   while (more)
   {  if (!perr) perr = main__paintbit(&r);
      if (err = wimp_get_rectangle(&r, &more), err) return err;
   }

   return perr;
}

static void main__handler(wimp_eventstr *e, void *handle)
{  switch (e->e)
   {  case wimp_EREDRAW:
         wimpt_complain(main__redraw());
         break;

      case wimp_EOPEN:
         wimpt_complain(wimp_open_wind(&e->data.o));
         break;
   }
}

static void main__gothelp(char *text)
{  if (strcmp(main__expand(text), main_helpbuf) != 0)
   {  strcpy(main_helpbuf, main_newbuf);
      wimpt_complain(main__format());
   }
}

static char *main__getstockhelp(int icn, int sub)
{  char token[32];
   char *msg;                 

   if (sub == 0)
      sprintf(token, "HelpI%d:(not found)", -icn);
   else
      sprintf(token, "HelpI%d%c:(not found)", -icn, 'a' + sub - 1);

   if (msg = lookup(token), strcmp(msg, "(not found)") == 0)
      return NULL;

   return msg;
}

static void main__fakehelp(void)
{  main__gothelp("");
}

static BOOL main__ukhandler(wimp_eventstr *e, void *handle)
{  intalk_msgstr *msg;

   switch (e->e)
   {  case wimp_ESEND:
      case wimp_ESENDWANTACK:
         if (help_process(e))  /* our help stuff! */
            return TRUE;
         else switch (e->data.msg.hdr.action)
         {  case wimp_MHELPREPLY:
               if (*e->data.msg.data.helpreply.text)
               {  char *hp = e->data.msg.data.helpreply.text;

                  for (hp = e->data.msg.data.helpreply.text; *hp >= ' '; hp++)
                     ;

                  *hp = '\0';   

                  /* Protect ourselves from unterminated strings */

                  main__gothelp(e->data.msg.data.helpreply.text);
               }
               else
                  main__fakehelp();
               return TRUE;

            case wimp_MHELPREQUEST:
               if (e->data.msg.data.helprequest.m.w == -2 &&
                   e->data.msg.data.helprequest.m.i == icon)
                  help_reply(msgs_lookup("ihelp")); 
               return TRUE;

            case (wimp_msgaction) intalk_MBALLOONSWITCH:
               msg = (intalk_msgstr *) &e->data.msg;
               active = msg->data.balloonswitch.show;                           
               icon = baricon_newsprite(active ? "active" : "inactive");
               msg->hdr.your_ref = msg->hdr.my_ref;
               wimpt_complain(wimp_sendmessage(wimp_EACK, &e->data.msg, msg->hdr.task));
               return TRUE;

            case (wimp_msgaction) intalk_MBALLOONRQSTATE:
               msg = (intalk_msgstr *) &e->data.msg;
               if (msg->hdr.task != wimpt_task())
               {  msg->hdr.action = (wimp_msgaction) intalk_MBALLOONSTATEIS;
                  msg->data.balloonswitch.show = active;
                  msg->hdr.your_ref = msg->hdr.my_ref;
                  wimpt_complain(wimp_sendmessage(wimp_ESEND, &e->data.msg, msg->hdr.task));
               }
               return TRUE;

            /* Means we're already loaded */   

            case (wimp_msgaction) intalk_MBALLOONSTATEIS:
               exit(0);
               return TRUE;
         }
         break;

      case wimp_EACK:
         switch (e->data.msg.hdr.action)
         {  case wimp_MHELPREQUEST:
               main__fakehelp();
               return TRUE;
         }
         break;
   }

   return FALSE;
}

static void main__alarm(int calledat, void *handle)
{  wimp_mousestr mse;
   char stocktext[200], *st;
   int sub = 0;
   static int click;

   calledat = calledat;

   wimpt_noerr(wimp_get_point_info(&mse));

   if (++click >= 10 && mse.w != -1 && mse.w != panel && mse.w != shadow)
   {  wimp_msgstr msg;

      if (mse.w > 0 && mse.i <= -2 && mse.i >= -13)
      {  *stocktext = '\0';
   
         st = main__getstockhelp(mse.i, sub++);
         while (st)
         {  strcat(stocktext, st);
            st = main__getstockhelp(mse.i, sub++);
         }   
   
         main__gothelp(stocktext);
      }
      else
      {  msg.hdr.action = wimp_MHELPREQUEST;
         msg.hdr.size   = sizeof(wimp_msghdr) + sizeof(wimp_mousestr);
         msg.data.helprequest.m = mse;
   
         wimpt_complain(wimp_sendwmessage(wimp_ESENDWANTACK, &msg, mse.w, mse.i));
      }
      click = 0;
   }

   wimpt_noerr(main__update(mse.x, mse.y, -1, bbc_inkey(-1)));

   alarm_set(alarm_timenow() + (active ? 1 : 50), main__alarm, handle);
}

static os_error *main__fontinfo(void)
{  os_error *err;
   font tf;
   font_info info;

   if (err = main__findfont(main__text, &tf), err) return err;
   if (err = font_readinfo(tf, &info), err) goto fail;

   lspc = (info.maxy - info.miny) + 2;
   base = -info.miny; /* height above baseline */

   font_lose(tf);
   return NULL;

fail:
   font_lose(tf);
   return err;

}

static void main__barclick(wimp_i i)
{  i = i;

   active = !active;
   icon = baricon_newsprite(active ? "active" : "inactive");
}

static os_error *main__info(void)
{  dbox inf;

   if (inf = dbox_new("progInfo"), !inf)
      return misc_err("main2");

   dbox_raw_eventhandler(inf, help_dboxrawevents, "info");

   dbox_setfield(inf, 3, "1.00 (29 Apr 94)");
   dbox_show(inf);
   dbox_fillin(inf);
   dbox_dispose(&inf);

   return NULL;      
}

static void main__menuhandler(void *handle, char *hit)
{  handle = handle;

   switch (hit[0])
   {  case menu_INFO:
         if (hit[1]) wimpt_complain(main__info());
         break;

      case menu_QUIT:
         exit(0);
         break;
   }
}

static wimp_msgaction okm[] =
{  wimp_MDATASAVE, wimp_MDATASAVEOK, wimp_MDATALOAD, wimp_MDATALOADOK,
   wimp_MDATAOPEN, wimp_MRAMFETCH, wimp_MRAMTRANSMIT, wimp_MPREQUIT,
   wimp_PALETTECHANGE, wimp_SAVEDESK, wimp_MDEVICECLAIM, wimp_MDEVICEINUSE,
   wimp_MDATASAVED, wimp_MMENUWARN, wimp_MMODECHANGE, wimp_MSLOTCHANGE,
   wimp_MSETSLOT, wimp_MTASKNAMERQ, wimp_MTASKNAMEIS, wimp_MTASKSTARTED,
   wimp_MHELPREQUEST, wimp_MHELPREPLY, wimp_MPrintFile, wimp_MWillPrint,
   wimp_MPrintTypeOdd, wimp_MPrintTypeKnown, wimp_MPrinterChange,
   (wimp_msgaction) intalk_MBALLOONSWITCH,
   (wimp_msgaction) intalk_MBALLOONRQSTATE,
   (wimp_msgaction) intalk_MBALLOONSTATEIS,
   (wimp_msgaction) 0
};

static os_error *main__getver(char *varname)
{  os_regset regs;
   os_error *err;
   char varbuf[20];

   regs.r[0] = (int) varname;
   regs.r[1] = (int) varbuf;
   regs.r[2] = 19;
   regs.r[3] = 0;
   regs.r[4] = 3;

   if (err = os_swix(0x23 /* OS_ReadVarVal */, &regs), err) return err;
   varbuf[regs.r[2]] = '\0';

   main__wimpver = atoi(varbuf);

   return NULL;
}

int main(void)
{  intalk_msgstr msg;
   main__font *downgrade;
   char tfont[256];
   int  fontsize;

   wimpt_noerr(main__getver("Explain$WimpVer")); 

#ifdef LIBTRACE
   trace_on();
#endif   

   wimpt_wimpversion(main__wimpver > 200 ? 300 : 200); 
   if (main__wimpver > 200) wimpt_messages(okm); 

   misc_init("Explain"); 
   flex_init(); 

   visdelay_init(); 
   visdelay_begin(); 

   res_init("Explain"); 

   resspr_init(); 
   alarm_init(); 

   msgs_init(); 
   wimpt_noerr(lookup_init("<Help$Msgs>"));   /* load help's token file */
   
   template_init(); 

   wimpt_noerr(options_init()); 

   wimpt_noerr(options_readstr("Explain$FontName", tfont, 255)); 
   if (*tfont == '\0') strcpy(tfont, main_TFONT); 

   wimpt_noerr(options_readint("Explain$FontSize", &fontsize)); 
   if (fontsize == 0) fontsize = main_FONTSIZE; 

   downgrade     = main__declarefont(main_DFONT, main_DSIZE, NULL); 
   main__text    = main__declarefont(tfont,      fontsize,   downgrade); 
   main__symbols = main__declarefont(main_SFONT, fontsize,   downgrade); 

   wimpt_noerr(main__fontinfo()); 

   if (__versionnumber() < 3.80)
      werr(TRUE, "Explain has been linked with an out of date RISC OS library"); 

   wimpt_noerr(main__mkwind(&panel, "panel")); 
   wimpt_noerr(main__mkwind(&shadow, "shadow")); 

   win_register_event_handler(panel, main__handler, NULL); 
   win_register_event_handler(shadow, main__handler, NULL); 
   win_add_unknown_event_processor(main__ukhandler, NULL); 

   alarm_set(alarm_timenow() + 1, main__alarm, NULL); 

   active = TRUE; 
   icon = baricon("active", (int) resspr_area(), main__barclick); 

   event_attachmenu(win_ICONBAR,
                    menu_new("Explain", msgs_lookup("main1")),
                    main__menuhandler, NULL); 

   help_register_handler(help_simplehandler, "ihelp"); 

   msg.hdr.action = (wimp_msgaction) intalk_MBALLOONRQSTATE; 
   msg.hdr.size   = sizeof(wimp_msghdr) + sizeof(intalk_balloonswitch); 

   wimpt_noerr(wimp_sendmessage(wimp_ESEND, (wimp_msgstr *) &msg, (wimp_t) 0)); 

   visdelay_end(); 

   for (;;)
      event_process();
}
