/*
 * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
 * 
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */ 

#define HELP_C

#include "mutt.h"
#include "mutt_curses.h"
#include "keymap.h"

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

static struct binding_t *help_lookupFunction (int op, int menu)
{
  int i;
  struct binding_t *map;

  if (menu != MENU_PAGER)
  {
    /* first look in the generic map for the function */
    for (i = 0; OpGeneric[i].name; i++)
      if (OpGeneric[i].op == op)
	return (&OpGeneric[i]);    
  }

  switch (menu)
  {
    case MENU_PAGER:
      map = OpPager;
      break;
    case MENU_MAIN:
      map = OpMain;
      break;
    case MENU_ATTACH:
      map = OpAttach;
      break;
    case MENU_POST:
      map = OpPost;
      break;
    case MENU_FOLDER:
      map = OpBrowser;
      break;
    case MENU_COMPOSE:
      map = OpCompose;
      break;
    case MENU_ALIAS:
      map = OpAlias;
      break;
#ifdef _PGPPATH
    case MENU_PGP:
      map = OpPgp;
      break;
#endif
    default:
      return NULL;
  }
  for (i = 0; map[i].name; i++)
    if (map[i].op == op)
      return (&map[i]);
  return (NULL);
}

void mutt_make_help (char *d, size_t dlen, char *txt, int menu, int op)
{
  char buf[SHORT_STRING];

  if (km_expand_key (buf, sizeof(buf), km_find_func (menu, op)))
    snprintf (d, dlen, "%s:%s", buf, txt);
  else
    d[0] = 0;
}

static void print_macro (FILE *f, const char *macro)
{
  int i;

  for (i = 0; *macro && i < COLS - 34; macro++, i++)
  {
    switch (*macro)
    {
      case '\033':
	fputs ("\\e", f);
	i++;
	break;
      case '\n':
	fputs ("\\n", f);
	i++;
	break;
      case '\r':
	fputs ("\\r", f);
	i++;
	break;
      case '\t':
	fputs ("\\t", f);
	i++;
	break;
      default:
	fputc (*macro, f);
	break;
    }
  }
}

static void dump_menu (FILE *f, int menu)
{
  struct keymap_t *map;
  struct binding_t *b;
  char buf[SHORT_STRING];

  /* browse through the keymap table */
  for (map = Keymaps[menu]; map; map = map->next)
  {
    km_expand_key (buf, sizeof (buf), map);

    if (map->op == OP_MACRO)
    {
      fprintf (f, "%s\t%-20s\t", buf, "macro");
      print_macro (f, map->macro);
      fputc ('\n', f);
    }
    else if (map->op != OP_NULL)
    {
      b = help_lookupFunction (map->op, menu);
      fprintf (f, "%s\t%-20s\t%s\n", buf,
	      b ? b->name : "UNKNOWN",
	      b ? HelpStrings[b->op] : "ERROR: please report this bug");
    }
  }
}

static int is_bound (struct keymap_t *map, int op)
{
  for (; map; map = map->next)
    if (map->op == op)
      return 1;
  return 0;
}

static void dump_unbound (FILE *f,
			  struct binding_t *funcs,
			  struct keymap_t *map,
			  struct keymap_t *aux)
{
  int i;

  for (i = 0; funcs[i].name; i++)
  {
    if (! is_bound (map, funcs[i].op) &&
	(!aux || ! is_bound (aux, funcs[i].op)))
      fprintf (f, "%s\t\t%s\n", funcs[i].name, HelpStrings[funcs[i].op]);
  }
}

void mutt_help (int menu)
{
  char t[_POSIX_PATH_MAX];
  char buf[SHORT_STRING];
  char *desc;
  FILE *f;
  struct binding_t *funcs = NULL;

  mutt_mktemp (t);
  if ((f = fopen (t, "w")) == NULL)
  {
    mutt_perror (t);
    return;
  }

  switch (menu)
  {
    case MENU_PAGER:
      funcs = OpPager;
      desc = "pager";
      break;
    case MENU_MAIN:
      funcs = OpMain;
      desc = "index";
      break;
    case MENU_ATTACH:
      funcs = OpAttach;
      desc = "attach";
      break;
    case MENU_COMPOSE:
      funcs = OpCompose;
      desc = "compose";
      break;
    case MENU_FOLDER:
      funcs = OpBrowser;
      desc = "browser";
      break;
    case MENU_ALIAS:
      funcs = OpAlias;
      desc = "alias";
      break;
    case MENU_POST:
      funcs = OpPost;
      desc = "postponed";
      break;
#ifdef _PGPPATH
    case MENU_PGP:
      funcs = OpPgp;
      desc = "pgp";
      break;
#endif
    default:
      desc = "<UNKNOWN MENU>";
  }

  dump_menu (f, menu);
  if (menu != MENU_EDITOR && menu != MENU_PAGER)
  {
    fputs ("\nGeneric bindings:\n\n", f);
    dump_menu (f, MENU_GENERIC);
  }

  fputs ("\nUnbound functions:\n\n", f);
  if (funcs)
    dump_unbound (f, funcs, Keymaps[menu], NULL);
  if (menu != MENU_PAGER)
    dump_unbound (f, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu]);

  fclose (f);

  snprintf (buf, sizeof (buf), "Help for %s", desc);
  mutt_do_pager (buf, t, 0);
}
