/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <opencm.h>
#include "opencmclient.h"
#include "opencm-builddir.h"

#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
#ifdef HAVE_TERM_H
#include <term.h>
#endif

#define FN_BOLD   0x1u
#define FN_ITALIC 0x2u

unsigned cur_mode = 0;

static void decide_clearmode(unsigned want_mode);

static void (*setcmode)(unsigned want_mode);
static void (*clearmode)(unsigned want_mode) = decide_clearmode;

static void
ansi_setmode(unsigned want_mode)
{
  if (want_mode != cur_mode) {
    if (want_mode & FN_BOLD) {
      if ((cur_mode & FN_BOLD) == 0) {
	fprintf(stdout, "\033[1m");
	cur_mode |= FN_BOLD;
      }
    }
    if (want_mode & FN_ITALIC) {
      if ((cur_mode & FN_ITALIC) == 0) {
	fprintf(stdout, "\033[4m");
	cur_mode |= FN_ITALIC;
      }
    }
  }
}

static void
ansi_clearmode(unsigned want_mode)
{
  /* This is a bit nasty. We want to turn off everything that is being
     turned off, but we do not yet wish to enable things in want_mode
     that are not currently turned ON. To make life really fun, ANSI
     terminal standard clears all modes at once.

     We therefore cheat by ANDing want_mode with cur_mode, turning
     everything off, and then re-enabling the pieces we want to
     keep. */
  want_mode &= cur_mode;

  fprintf(stdout, "\033[m");
  cur_mode = 0;
  ansi_setmode(want_mode);
}

static void
ascii_clearmode(unsigned want_mode)
{
  if (want_mode != cur_mode) {
    if ((want_mode & FN_ITALIC) == 0) {
      if (cur_mode & FN_ITALIC) {
	fprintf(stdout, "_");
	cur_mode &= ~FN_ITALIC;
      }
    }
    if ((want_mode & FN_BOLD) == 0) {
      if (cur_mode & FN_BOLD) {
	fprintf(stdout, "*");
	cur_mode &= ~FN_BOLD;
      }
    }
  }
}

static void
ascii_setmode(unsigned want_mode)
{
  if (want_mode != cur_mode) {
    if (want_mode & FN_BOLD) {
      if ((cur_mode & FN_BOLD) == 0) {
	fprintf(stdout, "*");
	cur_mode |= FN_BOLD;
      }
    }
    if (want_mode & FN_ITALIC) {
      if ((cur_mode & FN_ITALIC) == 0) {
	fprintf(stdout, "_");
	cur_mode |= FN_ITALIC;
      }
    }
  }
}

static void
decide_clearmode(unsigned want_mode)
{
  setcmode = ascii_setmode;
  clearmode = ascii_clearmode;

#ifdef HAVE_LIBTERMCAP
  if (isatty(1) &&
      tgetent(NULL, getenv("TERM")) > 0 &&
      tgetstr("md", NULL) > 0) {

    setcmode = ansi_setmode;
    clearmode = ansi_clearmode;
  }
#endif

  clearmode(want_mode);
}

static void
DisplayHelp(FILE *f)
{
  int c;

  cur_mode = 0;

  while ((c = fgetc(f)) != EOF) {
    if (c == '@') {
      c = fgetc(f);

      switch (c) {
      case '@':
	putchar('@');
	break;

      case 'C':
	{
	  /* Shap added this to support copyright notices. Yuck. */
	  do {
	    c = fgetc(f);
	  } while(c != '\n');
	  break;
	}
      case 'b': 
	{
	  c = fgetc(f);
	  if (c != '{')
	    THROW(ExBadValue, "Mis-formatted help file");

	  setcmode(cur_mode | FN_BOLD);

	  DisplayHelp(f);

	  clearmode (cur_mode & ~FN_BOLD);
	  break;
	}
      case 'i': 
	{
	  c = fgetc(f);
	  if (c != '{')
	    THROW(ExBadValue, "Mis-formatted help file");

	  setcmode(cur_mode | FN_ITALIC);

	  DisplayHelp(f);

	  clearmode (cur_mode & ~FN_ITALIC);
	  break;
	}
      }
    }
    else if (c == '}')
      return;
    else
      putchar(c);
  }
}

void
ShowHelp(const char *basename)
{
  const char *path;
  FILE *f;

  path = path_join("help", basename);

  /* Following code is here for testing purposes, because help files
   * cannot be tested if not installed: */
  TRY {
    const char *build_help_path = path_join("src", path);
    build_help_path = path_join(CM_BUILD_PWD, build_help_path);

    f = xfopen(build_help_path, 'r', 't');
  }
  DEFAULT(ex) {
    f = NULL;
  }
  END_CATCH;

  if (f == NULL) {
    path = path_join(CM_APPNAME, path);
    path = path_join(DATADIR, path);

    f = xfopen(path, 'r', 't');
  }

  clearmode(0);

  DisplayHelp(f);
}
