/****************************************************************/
/*                                                              */
/*                           helpsys.c                          */
/*                     Bad Seal help system                     */
/*                             v0.1                             */
/*                                                              */
/*        (c) 2001 Kostas Michalopoulos aka Bad Sector          */
/*                                                              */
/****************************************************************/

#include "allegro.h"
#include "seal.h"
#include "iodlg.h"
#include "helpsys.h"

#ifdef Bad_Seal
#include "registry.h"
#endif

/* dies with back button after hitting contents - probably trashing alloced/strduped memory somewhere. */

l_color flat;
l_color text;
l_color face3d;
l_color light3d;
l_color shadow3d;
l_color dark3d;
l_color link_color;

p_helpbox (*helpbox_init)(p_helpbox o, t_rect r)=&_helpbox_init;

typedef struct t_topic
{
  char name[51];
  int pos, size;
} t_topic;

typedef struct t_head
{
  char ID[5];
  int version;
  int topics;
} t_head;

static p_list get_file_items()
{
  p_list p = list_init(_malloc(sizeof(t_list)), &free_filehistory_item, 0);

  if (p) {
     p->insert(p, new_filehistory_item("Help files (*.hlp)", "*.hlp"));
     p->insert(p, new_filehistory_item("All files (*.*)", "*.*"));
  };

  return p;
}

#ifndef Bad_Seal
void draw_bevel(BITMAP *out, int x1, int y1, int x2, int y2, int c1, int c2)
{
  if (out) {
    line(out, x1, y1, x2, y1, c1);
    line(out, x1, y1, x1, y2, c1);
    line(out, x2, y1, x2, y2, c2);
    line(out, x1, y2, x2, y2, c2);
  }
}
#else
void draw_bevel(BITMAP *out, int x1, int y1, int x2, int y2, int c1, int c2);
/* this function is exported from Bad Controls */
#endif

/* begin of draw_string */
#define draw_string \
  s[sc] = 0; \
  xx = x+FONT_GETSTRWIDTH(VIEW(o)->font, s, -1); \
  if (xx >= mx-5) { \
    xx = 5+FONT_GETSTRWIDTH(VIEW(o)->font, s, -1); \
    if (o->u_link) o->link[o->links].x2 = x; \
    x = 5; \
    y += FONT_GETHEIGHT(VIEW(o)->font); \
    if (o->u_link) { \
      o->links++; \
      o->link[o->links].where = strdup(o->link[o->links-1].where); \
      o->link[o->links].x1 = 5; \
      o->link[o->links].y1 = y; \
      o->link[o->links].x2 = 6; \
      o->link[o->links].y2 = y+FONT_GETHEIGHT(VIEW(o)->font); \
    }; \
  }; \
  if (y > 0) { \
    l_int align = TX_ALIGN_LEFT; \
    l_color color = text; \
    if (o->u_link) { \
      align |= TX_UNDERLINE; \
      color = link_color; \
    }; \
    textout_draw(d, VIEW(o)->font, s, -1, x, y, align, color, flat); \
    if (o->u_bold) \
      textout_draw_rect(d, VIEW(o)->font, s, -1, x+1, y, x+1, y, align, color, TX_NOCOLOR, FALSE); \
  }; \
  x = xx; if (o->u_bold) x++;\
  s[0] = 0; \
  sc = 0;
/* end of draw_string */

void helpbox_parse_cmd(p_helpbox o, BITMAP *d, t_rect r, t_point p, l_text s, l_int *scp, l_text cmd)
{
  l_int i, x = o->f_x, y = o->f_y, mx = o->f_mx;
/* UNUSED
  l_int my = o->f_my
*/
  l_int sc = *scp;
  l_char par[16][256];
  l_int pars = 0;
  l_int argc = 0;
  l_bool u_str = FALSE;

  for (i=0;i<16;i++) par[i][0] = 0;
  for (i=0;i<strlen(cmd);i++) {
    switch (cmd[i]) {
      case ' ': if (!u_str) {
        if (argc > 0) {
          par[pars][argc] = 0;
          pars++;
          argc = 0;
        };
      } else {
        par[pars][argc] = cmd[i];
        argc++;
      }; break;
      case '"': {
        u_str = !u_str;
      }; break;
      default: {
        par[pars][argc] = cmd[i];
        argc++;
      }; break;
    };
  };
  if (argc > 0) par[pars][argc] = 0;

  if (!stricmp(par[0], "BR")) {
    l_int xx;
    draw_string;
    if (o->u_link) o->link[o->links].x2 = x;
    x = 5;
    y += FONT_GETHEIGHT(VIEW(o)->font);
    if (o->u_link) {
      o->links++;
      o->link[o->links].where = strdup(o->link[o->links-1].where);
      o->link[o->links].x1 = 5;
      o->link[o->links].y1 = y;
      o->link[o->links].x2 = 6; /* will changed by <LE> */
      o->link[o->links].y2 = y+FONT_GETHEIGHT(VIEW(o)->font);
    };
  };
  if (!stricmp(par[0], "L")) {
    l_int xx;
    draw_string;
    if (!o->u_link) o->links++;
    o->link[o->links].where = strdup(par[1]);
    o->link[o->links].x1 = x;
    o->link[o->links].y1 = y;
    o->link[o->links].x2 = x+1; /* will changed by <LE> */
    o->link[o->links].y2 = y+FONT_GETHEIGHT(VIEW(o)->font);
    o->u_link = TRUE;
  };
  if (!stricmp(par[0], "LE") && o->u_link) {
    l_int xx;
    draw_string;
    o->link[o->links].x2 = x;
    o->u_link = FALSE;
  };
  if (!stricmp(par[0], "B")) {
    l_int xx;
    draw_string;
    o->u_bold = TRUE;
  };
  if (!stricmp(par[0], "/B")) {
    l_int xx;
    draw_string;
    o->u_bold = FALSE;
  };
  if (!stricmp(par[0], "BUTTON")) {
    o->request(o, HBREQ_BUTTON, par[1], NULL, NULL);
  };

  o->f_x = x;
  o->f_y = y;
  *scp = sc;
}

void helpbox_draw_text(p_helpbox o, BITMAP *out, t_rect r, t_point p)
{
  l_int i, x = 5, y = 5-o->f_prg;
  BITMAP *d = create_bitmap(rect_sizex(r)-3, rect_sizey(r)-3);
  l_int mx = rect_sizex(r)-3, my = rect_sizey(r)-3;
  l_char s[1024];
  l_char cmd[1024];
  l_int sc = 0, cmdc = 0;
  l_bool u_tag = FALSE;
  l_bool u_str = FALSE;

  o->f_mx = mx;
  o->f_my = my;

  rectfill(d, 0, 0, mx, my, flat);

  s[0]=0;
  o->links = -1;
  for (i=0;i<strlen(o->text);i++) {
    switch (o->text[i]) {
      case '\n': break;
      case ' ': if (!u_tag) {
        l_int xx;
        s[sc] = 32;
        sc++;
        draw_string;
      } else {
        cmd[cmdc] = o->text[i];
        cmdc++;
      }; break;
      case '<': if (!u_str && o->text[i+1] != '<') {
        u_tag = TRUE;
        cmd[0] = 0;
        cmdc = 0;
      } else {
        if (o->text[i+1] == '<' && !u_str) i++;
        if (u_tag) {
          cmd[cmdc] = o->text[i];
          cmdc++;
        } else {
          s[sc] = o->text[i];
          sc++;
        };
      }; break;
      case '>': if (!u_str && u_tag) {
        u_tag = FALSE;
        cmd[cmdc] = 0;
        o->f_x = x;
        o->f_y = y;
        if (cmdc != 0) helpbox_parse_cmd(o, d, r, p, s, &sc, cmd);
        x = o->f_x;
        y = o->f_y;
      } else {
        if (u_tag) {
          cmd[cmdc] = o->text[i];
          cmdc++;
        } else {
          s[sc] = o->text[i];
          sc++;
        };
      }; break;
      case '"': if (u_tag) {
        u_str = !u_str;
        s[sc] = '"';
        sc++;
      } else {
        s[sc] = '"';
        sc++;
      }; break;
      default: {
        if (u_tag) {
          cmd[cmdc] = o->text[i];
          cmdc++;
        } else {
          s[sc] = o->text[i];
          sc++;
        };
      } break;
    };
    if (y > my && o->f_maxy > 0) break;
  };
  if (sc != 0) {
    l_int xx;
    draw_string;
  };
  if (o->f_maxy == 0) o->f_maxy = y;

  blit(d, out, 0, 0, r.a.x+p.x+2, r.a.y+p.y+2, mx, my);
  destroy_bitmap(d);
}

void helpbox_draw(p_view o)
{
  t_rect r = VIEW(o)->get_local_extent(VIEW(o));
  t_point p;
  BITMAP *out = VIEW(o)->begin_paint(VIEW(o), &p, r);
  if (out) {
/* UNUSED
    l_int i;
*/
    draw_bevel(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, shadow3d, light3d);
    draw_bevel(out, r.a.x+p.x+1, r.a.y+p.y+1, r.b.x+p.x-1, r.b.y+p.y-1, dark3d, face3d);
    helpbox_draw_text(HELPBOX(o), out, r, p);
  };
  VIEW(o)->end_of_paint(VIEW(o), r);
}

void helpbox_set_state(p_object o, l_dword st, l_bool set)
{
  view_set_state(o, st, set);
  if (st & OB_SF_FOCUSED) {
  };
}

void helpbox_handle_event(p_object o, t_event *event)
{
  view_translate_event(o, event);

  RETVIEW(o, event);

  if (event->type & EV_KEYBOARD && o->is_state(o, OB_SF_FOCUSED)) {
    if (OBJECT(keyb)->state & KB_SF_KEYDOWN) {
      switch (KEY_TO(keyb->code)) {
        case KB_DOWN: if (HELPBOX(o)->f_prg < (HELPBOX(o)->f_maxy-rect_sizey(VIEW(o)->bounds)+35)) {
          HELPBOX(o)->f_prg += 30;
          VIEW(o)->draw(VIEW(o));
          clear_event(event);
        }; break;
        case KB_UP: if (HELPBOX(o)->f_prg > 0) {
          HELPBOX(o)->f_prg -= 30;
          VIEW(o)->draw(VIEW(o));
          clear_event(event);
        }; break;
      };
    };
  };

  if ((event->type & EV_MOUSE) && (OBJECT(mouse)->state & MO_SF_MOUSEMOVE || OBJECT(mouse)->state & MO_SF_MOUSELPRESS)) {
    l_int i;
    t_point p = VIEW(o)->get_global_point(VIEW(o), point_assign(0, 0));
    t_rect r = VIEW(o)->get_local_extent(VIEW(o));
    p_helpbox ho = HELPBOX(o);
    l_int cur = CUR_ARROW;
    l_int lnk = -1;
    l_bool mouse_pressed = FALSE;

    for (i=0;i<=ho->links;i++)
      if (mouse->where.x >= ho->link[i].x1+r.a.x+p.x && mouse->where.y >= ho->link[i].y1+r.a.y+p.y &&
          mouse->where.x <= ho->link[i].x2+r.a.x+p.x && mouse->where.y <= ho->link[i].y2+r.a.y+p.y) {
        cur = CUR_FINGER;
        lnk = i;

        break;
      };

    if (VIEW(o)->cursor != cur) {
      VIEW(o)->cursor = cur;
      VIEW(o)->set_mouse_cursor(VIEW(o));
    };

    mouse_pressed = (OBJECT(mouse)->state & MO_SF_MOUSELPRESS);

    while (OBJECT(mouse)->state & MO_SF_MOUSELPRESS) {
      o->get_event(o, event);
    };
    clear_event(event);

    if (cur == CUR_FINGER && mouse_pressed) {
      HELPBOX(o)->navigate(HELPBOX(o), HELPBOX(o)->link[lnk].where);
    };
  };
}

void helpbox_navigate(p_helpbox o, l_text where)
{
  __p_hbgbinfo tmp = o->f_back;
  if (!where) return;

  if (!stricmp(where, "HELP:ABOUT")) {
    if (o->text) _free(o->text);
    if (o->topic) _free(o->topic);
    o->topic = strdup("help:about");

    o->text = strdup("<b>Bad Seal Help System version 0.1</b><br><br>
  HELP (Bad Seal Help System), was made at 20.04.2001 by Kostas Michalopoulos
 aka Bad Sector, mostly for use under Bad Seal GUI. But you can find HELP
 also for SEAL 1.0E and other compatible versions and distributions (like
 StarSeal).<br><br>
  For more information on how to write your own HELP (*.HLP) files, see
 HELP's documentation.<br><br>
  You can contact me at:<br>
    e-mail:<br>
      badsector@badseal.org<br>
    www:<br>
      (my personal page)<br>
      http://me.badseal.org/<br>
      (SEAL/Bad Seal resources page)<br>
      http://www.badseal.org/<br>
");
  } else if (!stricmp(where, "HELP:NONE")) {
    if (o->text) _free(o->text);
    if (o->topic) _free(o->topic);
    o->topic = strdup("help:none");
  } else if (!stricmp(where, "HELP:OPEN")) {
    l_text file;
    file = open_dialog("./", "*.hlp", get_file_items());
    if (file) {
      if (o->filename) _free(o->filename);
      o->filename = file;
      o->navigate(o, "main");
      return;
    };
  } else if (!stricmp(where, "HELP:BACK")) {
    if (!o->f_back || !o->f_back->prev) {
      msgbox(MW_INFO, MB_OK, "Cannot go back");
    } else o->go_back(o);
    return;
  } else if (!o->filename) {
    if (o->text) _free(o->text);
    if (o->topic) _free(o->topic);
    o->text = strdup("<b>Error: topic without file specified");
  } else {
    FILE *f;
    t_topic rec;
    t_head head;
    l_int i, pos;
    l_text s;

    if (o->text) _free(o->text);
    if (o->topic) _free(o->topic);
    if (!strcmp(o->filename, "")) {
      f = NULL;
    } else {
      f = fopen(o->filename, "rb");
    };

    if (!f) {
      o->text = strdup("<b>Error: file not found, cannot open file or file not specified");
    } else {
      l_bool ok = TRUE;
      fread(&head, sizeof(t_head), 1, f);
      if (head.ID[0] != 'B' || head.ID[1] != 'S' || head.ID[2] != 'H' ||
          head.ID[3] != 'L' || head.ID[4] != 'P') {
        o->text = strdup("<b>Error: invalid file header or file is damaged");
        ok = FALSE;
      };
      if (head.version > 0) {
        o->text = strdup("<b>Error: unknown file version");
        ok = FALSE;
      };
      if (head.topics == 0) {
        o->text = strdup("<b>Error: file without topics");
        ok = FALSE;
      };
      if (ok) {
        pos = 0;
        for (i=0;i<head.topics;i++) {
          fread(&rec, sizeof(t_topic), 1, f);
          if (!stricmp(rec.name, where)) {
            pos = rec.pos;
            break;
          };
        };
        if (pos == 0) {
          o->text = strdup("<b>Error: topic not found");
          ok = FALSE;
        };
      };
      if (ok) {
        fpos_t ps = pos;
        fsetpos(f, &ps);
        s = (l_text) _malloc(rec.size+1);
        fread(s, rec.size, 1, f);
        s[rec.size] = 0;
        o->text = s;
        o->topic = _strdup(where);
/*        o->text = (l_text) _malloc(rec.size+1);
        memcpy(o->text, s, rec.size);
        o->text[rec.size]=0;
        _free(s);
*/
      };
      fclose(f);
    };
  };

  if (!o->text) o->text = _strdup("");

  o->f_maxy = 0;
  o->f_prg = 0;

  VIEW(o)->draw(VIEW(o));

  if (!o->f_avoid_bi) {
    o->f_back = (__p_hbgbinfo) _malloc(sizeof(__t_hbgbinfo));
    o->f_back->where = _strdup(where);
    o->f_back->prev = tmp;
  } else {
    o->f_avoid_bi = FALSE;
  };
}

l_bool helpbox_request(p_helpbox o, l_int request, void *data1, void *data2, void **rdata1)
{
  return TRUE;
}

void helpbox_go_back(p_helpbox o)
{
  __p_hbgbinfo tmp;
  if (!o || !o->f_back || !o->f_back->prev) return;
  tmp = o->f_back;
  if (o->f_back->prev->where) {
    o->f_avoid_bi = TRUE;
    o->navigate(o, o->f_back->prev->where);
  };
  o->f_back = o->f_back->prev;
  _free(tmp);
}

p_helpbox _helpbox_init(p_helpbox o, t_rect r)
{
  if (!o) return NULL;
  clear_type(o, sizeof(t_helpbox));
  view_init(&o->obclass, r);

  OBJECT(o)->translate_event = &helpbox_handle_event;
  OBJECT(o)->set_state = &helpbox_set_state;

  VIEW(o)->draw = &helpbox_draw;

  o->navigate = &helpbox_navigate;
  o->go_back = &helpbox_go_back;

  o->request = &helpbox_request;

  o->text = _strdup("Bad Seal help system<br>
No file loaded<br><br><br>
Click <l help:open>here<le> to open a help file.<br><br>
Click <l help:about popup>here<le> for more info about Help System.
");
  o->filename = NULL;
  o->topic = NULL;

  o->f_prg = 0;
  o->f_maxy = 0;
  o->f_back = NULL;
  o->f_avoid_bi = FALSE;

  return o;
}

void open_help(l_text filename)
{
  /* i can also run HELP.EXE with only filename specified, but this
   will also check if filename is in ./help if not in current directory. */
  open_help_topic(filename, "main");
}

void open_help_topic(l_text filename, l_text topic)
{
  FILE *f = fopen(filename, "rb");
  l_text fn = (l_text) _malloc(strlen(filename)+16);
  l_text args = (l_text) _malloc(strlen(filename)+strlen(topic)+2);

  if (!f) {
    strcpy(fn, "./help/");
    strcat(fn, filename);
    f = fopen(fn, "rb");
    if (f) filename = fn; else {
      _free(fn);
      _free(args);
      msgbox(MW_ERROR, MB_OK, "Cannot find help file - %s", filename);
      return;
    };
  };
  fclose(f);

  strcpy(args, filename);
  strcat(args, " ");
  strcat(args, topic);
  if (!run_file_args(HELP_EXE, args)) {
    msgbox(MW_ERROR, MB_OK, "Help System error: cannot find help viewer");
  };

  _free(args);
  _free(fn);
}

#include "helpsys.exp"

lib_begin(void)
{
  if (ap_process == AP_ALLOC)
  {
    AP_EXPORTLIB();
  } else
  if (ap_process == AP_INIT)
  {
    AP_SETNUMOFCALLS(1);

    #ifdef Bad_Seal
    flat = registry_color("flat_background");
    text = registry_color("edit_text");
    face3d = registry_color("3d_face");
    light3d = registry_color("3d_light");
    shadow3d = registry_color("3d_shadow");
    dark3d = registry_color("3d_dark");
    if (key_exists("current/seal/colors/link")) link_color = registry_color("3d_dark"); else link_color = COLOR(9);
    #else
    flat = color_get_from_ini("text_input_background");
    text = color_get_from_ini("text");
    face3d = color_get_from_ini("3D_background");
    light3d = COLOR(15);
    shadow3d = COLOR(0);
    dark3d = COLOR(0);
    link_color = COLOR(9);
    #endif
  };
} lib_end;

