/*
 * htmlutil.cc
 *
 * Utility functions for helping convert Latex files
 * into HTML files.
 * files.
 *
 * Julian Smart September 1993
 *
 */

#include <wx.h>
#include "tex2any.h"
#include "tex2rtf.h"

#include "readshg.h" // Segmented hypergraphics parsing

char *ChaptersName = NULL;
char *SectionsName = NULL;
char *SubsectionsName = NULL;
char *SubsubsectionsName = NULL;
char *TitlepageName = NULL;
char *lastFileName = NULL;
char *lastTopic = NULL;
char *currentFileName = NULL;

static TexChunk *descriptionItemArg = NULL;
static TexChunk *helpRefFilename = NULL;
static TexChunk *helpRefText = NULL;
static int indentLevel = 0;
static int citeCount = 1;
extern FILE *Contents;
FILE *Titlepage = NULL;
int fileId = 0;

// Are we in verbatim mode? If so, format differently.
static Bool inVerbatim = FALSE;

// This is defined in the Tex2Any library.
extern char *BigBuffer;

class HyperReference: public wxObject
{
 public:
  char *refName;
  char *refFile;
  HyperReference(char *name, char *file)
  {
    if (name) refName = copystring(name);
    if (file) refFile = copystring(file);
  }
};

class TexNextPage: public wxObject
{
 public:
  char *label;
  char *filename;
  TexNextPage(char *theLabel, char *theFile)
  {
    label = copystring(theLabel);
    filename = copystring(theFile);
  }
  ~TexNextPage(void)
  {
    delete[] label;
    delete[] filename;
  }
};

wxHashTable TexNextPages(wxKEY_STRING);

static char *CurrentChapterName = NULL;
static char *CurrentChapterFile = NULL;
static char *CurrentSectionName = NULL;
static char *CurrentSectionFile = NULL;
static char *CurrentSubsectionName = NULL;
static char *CurrentSubsectionFile = NULL;
static char *CurrentSubsubsectionName = NULL;
static char *CurrentSubsubsectionFile = NULL;

void SetCurrentChapterName(char *s, char *file)
{
  if (CurrentChapterName) delete[] CurrentChapterName;
  CurrentChapterName = copystring(s);
  if (CurrentChapterFile) delete[] CurrentChapterFile;
  CurrentChapterFile = copystring(file);

  currentFileName = CurrentChapterFile;
}
void SetCurrentSectionName(char *s, char *file)
{
  if (CurrentSectionName) delete[] CurrentSectionName;
  CurrentSectionName = copystring(s);
  if (CurrentSectionFile) delete[] CurrentSectionFile;
  CurrentSectionFile = copystring(file);

  currentFileName = CurrentSectionFile;
}
void SetCurrentSubsectionName(char *s, char *file)
{
  if (CurrentSubsectionName) delete[] CurrentSubsectionName;
  CurrentSubsectionName = copystring(s);
  if (CurrentSubsectionFile) delete[] CurrentSubsectionFile;
  CurrentSubsectionFile = copystring(file);
  currentFileName = CurrentSubsectionFile;
}
void SetCurrentSubsubsectionName(char *s, char *file)
{
  if (CurrentSubsubsectionName) delete[] CurrentSubsubsectionName;
  CurrentSubsubsectionName = copystring(s);
  if (CurrentSubsubsectionFile) delete[] CurrentSubsubsectionFile;
  CurrentSubsubsectionFile = copystring(file);
  currentFileName = CurrentSubsubsectionFile;
}

/*
 * Close former filedescriptor and reopen using another filename.
 *
 */

void ReopenFile(FILE **fd, char **fileName)
{
  if (*fd)
  {
    fprintf(*fd, "\n</HTML>\n");
    fclose(*fd);
  }
  fileId ++;
  char buf[400];
  if (truncateFilenames)
    sprintf(buf, "%s%d.htm", FileRoot, fileId);
  else
    sprintf(buf, "%s%d.html", FileRoot, fileId);
  if (*fileName) delete[] *fileName;
  *fileName = copystring(FileNameFromPath(buf));
  *fd = fopen(buf, "w");
  fprintf(*fd, "<HTML>\n");
}

/*
 * Given a TexChunk with a string value, scans through the string
 * converting Latex-isms into HTML-isms, such as 2 newlines -> <P>.
 *
 */
 
void ProcessText2HTML(TexChunk *chunk)
{
  Bool changed = FALSE;
  int ptr = 0;
  int i = 0;
  char ch = 1;
  int len = strlen(chunk->value);
  while (ch != 0)
  {
    ch = chunk->value[i];

    // 2 newlines means \par
    if (!inVerbatim && chunk->value[i] == 10 && ((len > i+1 && chunk->value[i+1] == 10) ||
                        ((len > i+1 && chunk->value[i+1] == 13) &&
                        (len > i+2 && chunk->value[i+2] == 10))))
    {
      BigBuffer[ptr] = 0; strcat(BigBuffer, "<P>\n\n"); ptr += 5;
      i += 2;
      changed = TRUE;
    }
    else if (!inVerbatim && ch == '`' && (len >= i+1 && chunk->value[i+1] == '`'))
    {
      BigBuffer[ptr] = '"'; ptr ++;
      i += 2;
      changed = TRUE;
    }
    else if (!inVerbatim && ch == '`') // Change ` to '
    {
      BigBuffer[ptr] = 39; ptr ++;
      i += 1;
      changed = TRUE;
    }
    else if (!inVerbatim && ch == '<') // Change < to &lt
    {
      BigBuffer[ptr] = 0;
      strcat(BigBuffer, "&lt;");
      ptr += 4;
      i += 1;
      changed = TRUE;
    }
    else if (!inVerbatim && ch == '>') // Change > to &gt
    {
      BigBuffer[ptr] = 0;
      strcat(BigBuffer, "&gt;");
      ptr += 4;
      i += 1;
      changed = TRUE;
    }
    else
    {
      BigBuffer[ptr] = ch;
      i ++;
      ptr ++;
    }
  }
  BigBuffer[ptr] = 0;

  if (changed)
  {
    delete chunk->value;
    chunk->value = copystring(BigBuffer);
  }
}

/*
 * Scan through all chunks starting from the given one,
 * calling ProcessText2HTML to convert Latex-isms to RTF-isms.
 * This should be called after Tex2Any has parsed the file,
 * and before TraverseDocument is called.
 *
 */
 
void Text2HTML(TexChunk *chunk)
{
  Tex2RTFYield();
  if (stopRunning) return;

  switch (chunk->type)
  {
    case CHUNK_TYPE_MACRO:
    {
      TexMacroDef *def = chunk->def;

      if (def && def->ignore)
        return;

      if (def && (def->macroId == ltVERBATIM))
        inVerbatim = TRUE;

      wxNode *node = chunk->children.First();
      while (node)
      {
        TexChunk *child_chunk = (TexChunk *)node->Data();
        Text2HTML(child_chunk);
        node = node->Next();
      }

      if (def && (def->macroId == ltVERBATIM))
        inVerbatim = FALSE;

      break;
    }
    case CHUNK_TYPE_ARG:
    {
      wxNode *node = chunk->children.First();
      while (node)
      {
        TexChunk *child_chunk = (TexChunk *)node->Data();
        Text2HTML(child_chunk);
        node = node->Next();
      }

      break;
    }
    case CHUNK_TYPE_STRING:
    {
      if (chunk->value)
        ProcessText2HTML(chunk);
      break;
    }
  }
}

/*
 * Add appropriate browse buttons to this page.
 *
 */

void AddBrowseButtons(char *upLabel, char *upFilename,
  char *previousLabel, char *previousFilename,
  char *thisLabel, char *thisFilename)
{
  if (htmlBrowseButtons == HTML_BUTTONS_NONE)
    return;

  char *contentsReference = NULL;
  if (htmlBrowseButtons == HTML_BUTTONS_TEXT)
    contentsReference = ContentsNameString;
  else
    contentsReference = "<img align=top src=\"contents.gif\" ALT=\"Contents\">";

  char *upReference = NULL;
  if (htmlBrowseButtons == HTML_BUTTONS_TEXT)
    upReference = UpNameString;
  else
    upReference = "<img align=top src=\"up.gif\" ALT=\"Up\">";

  char *backReference = NULL;
  if (htmlBrowseButtons == HTML_BUTTONS_TEXT)
    backReference = "&lt;&lt;";
  else
    backReference = "<img align=top src=\"back.gif\" ALT=\"Previous\">";

  char *forwardReference = NULL;
  if (htmlBrowseButtons == HTML_BUTTONS_TEXT)
    forwardReference = "&gt;&gt;";
  else
    forwardReference = "<img align=top src=\"forward.gif\" ALT=\"Next\">";
  
  char buf[200];

  /*
   * Contents button
   *
   */

  if (truncateFilenames)
    sprintf(buf, "\n<A HREF=\"%s.htm\">%s</A> ", FileNameFromPath(FileRoot), contentsReference);
  else
    sprintf(buf, "\n<A HREF=\"%s_contents.html\">%s</A> ", FileNameFromPath(FileRoot), contentsReference);
  TexOutput(buf);

  /*
   * Up button
   *
   */

  if (upLabel && upFilename)
  {
    if (strlen(upLabel) > 0)
      sprintf(buf, "<A HREF=\"%s#%s\">%s</A> ", upFilename, upLabel, upReference);
    else
      sprintf(buf, "<A HREF=\"%s\">%s</A> ", upFilename, upReference);
    TexOutput(buf);
  }

  /*
   * << button
   *
   */

  if (previousLabel && previousFilename)
  {
    sprintf(buf, "<A HREF=\"%s#%s\">%s</A> ", previousFilename, previousLabel, backReference);
    TexOutput(buf);
  }

  char *nextLabel = NULL;
  char *nextFilename = NULL;

  // Get the next page, and record the previous page's 'next' page
  // (i.e. this page)
  TexNextPage *nextPage = (TexNextPage *)TexNextPages.Get(thisLabel);
  if (nextPage)
  {
    nextLabel = nextPage->label;
    nextFilename = nextPage->filename;
  }
  if (previousLabel && previousFilename)
  {
    TexNextPage *oldNextPage = (TexNextPage *)TexNextPages.Get(previousLabel);
    if (oldNextPage)
    {
      delete oldNextPage;
      TexNextPages.Delete(previousLabel);
    }
    TexNextPage *newNextPage = new TexNextPage(thisLabel, thisFilename);
    TexNextPages.Put(previousLabel, newNextPage);
  }

  /*
   * >> button
   *
   */

  if (nextLabel && nextFilename)
  {
    sprintf(buf, "<A HREF=\"%s#%s\">%s</A> ", nextFilename, nextLabel, forwardReference);
    TexOutput(buf);
  }

  /*
   * Horizontal rule to finish it off nicely, and a break to add space
   * before the following title.
   *
   */
  TexOutput("<HR><BR>\n");

  // Update last topic/filename
  if (lastFileName)
    delete[] lastFileName;
  lastFileName = copystring(thisFilename);
  if (lastTopic)
    delete[] lastTopic;
  lastTopic = copystring(thisLabel);
}

// Called on start/end of macro examination
void HTMLOnMacro(int macroId, int no_args, Bool start)
{
  switch (macroId)
  {
  case ltCHAPTER:
  case ltCHAPTERSTAR:
  case ltCHAPTERHEADING:
  {
    if (!start)
    {
      sectionNo = 0;
      figureNo = 0;
      subsectionNo = 0;
      subsubsectionNo = 0;
      if (macroId != ltCHAPTERSTAR)
        chapterNo ++;

      SetCurrentOutput(NULL);
      startedSections = TRUE;

      char *topicName = FindTopicName(GetNextChunk());
      ReopenFile(&Chapters, &ChaptersName);
      AddTexRef(topicName, ChaptersName, ChapterNameString);

      SetCurrentChapterName(topicName, ChaptersName);

      SetCurrentOutput(Chapters);
      fprintf(Chapters, "<A NAME=\"%s\"><H2>", topicName);

      char titleBuf[150];
      if (truncateFilenames)
        sprintf(titleBuf, "%s.htm", FileNameFromPath(FileRoot));
      else
        sprintf(titleBuf, "%s_contents.html", FileNameFromPath(FileRoot));
      AddBrowseButtons("", titleBuf, // Up
                       lastTopic, lastFileName,  // Last topic
                       topicName, ChaptersName); // This topic

      TexOutput("<head><title>");
      OutputCurrentSection(); // Repeat section header
      TexOutput("</title></head>\n");

      SetCurrentOutputs(Contents, Chapters);
      fprintf(Contents, "\n<LI><A HREF=\"%s#%s\">", ChaptersName, topicName);

      OutputCurrentSection();
      fprintf(Contents, "</A>\n");
      fprintf(Chapters, "</H2></A>\n");

      SetCurrentOutput(Chapters);
    }
    break;
  }
  case ltSECTION:
  case ltSECTIONSTAR:
  case ltSECTIONHEADING:
  case ltGLOSS:
  {
    if (!start)
    {
      subsectionNo = 0;
      subsubsectionNo = 0;

      if (macroId != ltSECTIONSTAR)
        sectionNo ++;
        
      SetCurrentOutput(NULL);
      startedSections = TRUE;

      char *topicName = FindTopicName(GetNextChunk());
      ReopenFile(&Sections, &SectionsName);
      AddTexRef(topicName, SectionsName, SectionNameString);

      SetCurrentSectionName(topicName, SectionsName);

      SetCurrentOutput(Sections);
      fprintf(Sections, "<A NAME=\"%s\"><H2>", topicName);

      AddBrowseButtons(CurrentChapterName, CurrentChapterFile, // Up
                       lastTopic, lastFileName,  // Last topic
                       topicName, SectionsName); // This topic

      TexOutput("<head><title>");
      OutputCurrentSection();
      TexOutput("</title></head>\n");

      FILE *jumpFrom = ((DocumentStyle == LATEX_ARTICLE) ? Contents : Chapters);

      SetCurrentOutputs(jumpFrom, Sections);
      if (DocumentStyle == LATEX_ARTICLE)
        fprintf(jumpFrom, "\n<LI><A HREF=\"%s#%s\">", SectionsName, topicName);
      else
        fprintf(jumpFrom, "\n<A HREF=\"%s#%s\"><B>", SectionsName, topicName);

      OutputCurrentSection();

      if (DocumentStyle == LATEX_ARTICLE)
        fprintf(jumpFrom, "</A>\n");
      else
        fprintf(jumpFrom, "</B></A><BR>\n");
      fprintf(Sections, "</H2></A>\n");

      SetCurrentOutput(Sections);
    }
    break;
  }
  case ltSUBSECTION:
  case ltSUBSECTIONSTAR:
  case ltMEMBERSECTION:
  case ltFUNCTIONSECTION:
  {
    if (!start)
    {
      if (!Sections)
      {
        OnError("You cannot have a subsection before a section!");
      }
      else
      {
      subsubsectionNo = 0;

      if (macroId != ltSUBSECTIONSTAR)
        subsectionNo ++;

      SetCurrentOutput(NULL);
      startedSections = TRUE;

      char *topicName = FindTopicName(GetNextChunk());
      ReopenFile(&Subsections, &SubsectionsName);
      AddTexRef(topicName, SubsectionsName, SubsectionNameString);

      SetCurrentSubsectionName(topicName, SubsectionsName);

      SetCurrentOutput(Subsections);
      fprintf(Subsections, "<A NAME=\"%s\"><H2>", topicName);

      AddBrowseButtons(CurrentSectionName, CurrentSectionFile, // Up
                       lastTopic, lastFileName,  // Last topic
                       topicName, SubsectionsName); // This topic

      TexOutput("<head><title>");
      OutputCurrentSection();
      TexOutput("</title></head>\n");

      SetCurrentOutputs(Sections, Subsections);
      fprintf(Sections, "\n<A HREF=\"%s#%s\"><B>", SubsectionsName, topicName);
      OutputCurrentSection();
      fprintf(Sections, "</B></A><BR>\n");
      fprintf(Subsections, "</H2></A>\n");

      SetCurrentOutput(Subsections);
      }
    }
    break;
  }
  case ltSUBSUBSECTION:
  case ltSUBSUBSECTIONSTAR:
  {
    if (!start)
    {
      if (!Subsections)
      {
        OnError("You cannot have a subsubsection before a subsection!");
      }
      else
      {
        if (macroId != ltSUBSUBSECTIONSTAR)
          subsubsectionNo ++;

        SetCurrentOutput(NULL);
        startedSections = TRUE;

        char *topicName = FindTopicName(GetNextChunk());
        ReopenFile(&Subsubsections, &SubsubsectionsName);
        AddTexRef(topicName, SubsubsectionsName, SubsubsectionNameString);

        SetCurrentSubsubsectionName(topicName, SubsubsectionsName);

        SetCurrentOutput(Subsubsections);
        fprintf(Subsubsections, "<A NAME=\"%s\"><H3>", topicName);

        AddBrowseButtons(CurrentSubsectionName, CurrentSubsectionFile, // Up
                         lastTopic, lastFileName,  // Last topic
                         topicName, SubsubsectionsName); // This topic

        TexOutput("<head><title>");
        OutputCurrentSection();
        TexOutput("</title></head>\n");

        SetCurrentOutputs(Subsections, Subsubsections);
        fprintf(Subsections, "\n<A HREF=\"%s#%s\"><B>", SubsubsectionsName, topicName);
        OutputCurrentSection();
        fprintf(Subsections, "</B></A><BR>\n");
        fprintf(Subsubsections, "</H3></A>\n");

        SetCurrentOutput(Subsubsections);

      }
    }
    break;
  }
  case ltFUNC:
  case ltPFUNC:
  {
    SetCurrentOutput(Subsections);
    if (start)
    {
    }
    else
    {
    }
    break;
  }
  case ltCLIPSFUNC:
  {
    SetCurrentOutput(Subsections);
    if (start)
    {
    }
    else
    {
    }
    break;
  }
  case ltMEMBER:
  {
    SetCurrentOutput(Subsections);
    if (start)
    {
    }
    else
    {
    }
    break;
  }
  case ltVOID:
    if (start)
      TexOutput("<B>void</B>");
    break;
  case ltHARDY:
    if (start)
      TexOutput("HARDY");
    break;
  case ltWXCLIPS:
    if (start)
      TexOutput("wxCLIPS");
    break;
  case ltAMPERSAND:
    if (start)
      TexOutput("&amp;");
    break;
  case ltBACKSLASHCHAR:
    if (start)
      TexOutput("<BR>\n");
    break;
  case ltRTFSP:  // Explicit space, RTF only
    break;
  case ltSPECIALTILDE:
  {
    if (start)
    {
      if (inVerbatim)
        TexOutput("~");
      else
        TexOutput(" ");
    }
    break;
  }
  case ltITEMIZE:
  case ltENUMERATE:
  case ltDESCRIPTION:
  case ltTWOCOLLIST:
  {
    if (start)
    {
      indentLevel ++;

      int listType;
      if (macroId == ltENUMERATE)
        listType = LATEX_ENUMERATE;
      else if (macroId == ltITEMIZE)
        listType = LATEX_ITEMIZE;
      else
        listType = LATEX_DESCRIPTION;

      itemizeStack.Insert(new ItemizeStruc(listType));
      switch (listType)
      {
        case LATEX_ITEMIZE:
          TexOutput("<UL>\n");
          break;
        case LATEX_ENUMERATE:
          TexOutput("<OL>\n");
          break;
        case LATEX_DESCRIPTION:
        default:
          TexOutput("<DL>\n");
          break;
      }
    }
    else
    {
      indentLevel --;
      if (itemizeStack.First())
      {
        ItemizeStruc *struc = (ItemizeStruc *)itemizeStack.First()->Data();
        switch (struc->listType)
        {
          case LATEX_ITEMIZE:
            TexOutput("</UL>\n");
            break;
          case LATEX_ENUMERATE:
            TexOutput("</OL>\n");
            break;
          case LATEX_DESCRIPTION:
          default:
            TexOutput("</DL>\n");
            break;
        }

        delete struc;
        delete itemizeStack.First();
      }
    }
    break;
  }
  case ltPAR:
  {
    if (start)
      TexOutput("<P>\n");
    break;
  }
  case ltVERBATIM:
  {
    if (start)
    {
      char buf[100];
      sprintf(buf, "<PRE>\n");
      TexOutput(buf);
    }
    else TexOutput("</PRE>\n");
    break;
  }
  case ltCENTERLINE:
  case ltCENTER:
  {
    if (start)
    {
      TexOutput("<CENTER>");
    }
    else TexOutput("</CENTER>");
    break;
  }
  case ltFLUSHLEFT:
  {
/*
    if (start)
    {
      TexOutput("{\\ql ");
    }
    else TexOutput("}\\par\\pard\n");
*/
    break;
  }
  case ltFLUSHRIGHT:
  {
/*
    if (start)
    {
      TexOutput("{\\qr ");
    }
    else TexOutput("}\\par\\pard\n");
*/
    break;
  }
  case ltSMALL:
  {
/*
    if (start)
    {
      TexOutput("{\\fs16\n");
    }
    else TexOutput("}\n");
*/
    break;
  }
  case ltTINY:
  {
/*
    if (start)
    {
      TexOutput("{\\fs12\n");
    }
    else TexOutput("}\n");
*/
    break;
  }
  case ltNORMALSIZE:
  {
/*
    if (start)
    {
      TexOutput("{\\fs20\n");
    }
    else TexOutput("}\n");
*/
    break;
  }
  case ltlarge:
  {
/*
    if (start)
    {
      TexOutput("{\\fs24\n");
    }
    else TexOutput("}\n");
*/
    break;
  }
  case ltLARGE:
  {
    if (start)
    {
      TexOutput("<H1>");
    }
    else TexOutput("</H1>");
    break;
  }
  case ltLarge:
  {
    if (start)
    {
      TexOutput("<H1>");
    }
    else TexOutput("</H1>");
    break;
  }
  case ltBFSERIES:
  case ltTEXTBF:
  case ltBF:
  {
    if (start)
    {
      TexOutput("<B>");
    }
    else TexOutput("</B>");
    break;
  }
  case ltITSHAPE:
  case ltTEXTIT:
  case ltIT:
  {
    if (start)
    {
      TexOutput("<I>");
    }
    else TexOutput("</I>");
    break;
  }
  case ltEMPH:
  case ltEM:
  {
    if (start)
    {
      TexOutput("<EM>");
    }
    else TexOutput("</EM>");
    break;
  }
  case ltUNDERLINE:
  {
    if (start)
    {
      TexOutput("<UL>");
    }
    else TexOutput("</UL>");
    break;
  }
  case ltTTFAMILY:
  case ltTEXTTT:
  case ltTT:
  {
    if (start)
    {
      TexOutput("<TT>");
    }
    else TexOutput("</TT>");
    break;
  }
/*
  case ltSC:
  {
    break;
  }
*/
  case ltITEM:
  {
    if (!start)
    {
      wxNode *node = itemizeStack.First();
      if (node)
      {
        ItemizeStruc *struc = (ItemizeStruc *)node->Data();
        struc->currentItem += 1;
        if (struc->listType == LATEX_DESCRIPTION)
        {
          if (descriptionItemArg)
          {
            TexOutput("<DT> ");
            TraverseChildrenFromChunk(descriptionItemArg);
            TexOutput("\n");
            descriptionItemArg = NULL;
          }
          TexOutput("<DD>");
        }
        else
          TexOutput("<LI>");
      }
    }
    break;
  }
  case ltMAKETITLE:
  {
    if (start && DocumentTitle && DocumentAuthor)
    {
      // Add a special label for the contents page.
      TexOutput("<CENTER>\n");
      TexOutput("<A NAME=\"contents\">");
      TexOutput("<H2>\n");
      TraverseChildrenFromChunk(DocumentTitle);
      TexOutput("</H2>");
      TexOutput("<P>");
      TexOutput("</A>\n");
      TexOutput("<P>\n\n");
      TexOutput("<H3>");
      TraverseChildrenFromChunk(DocumentAuthor);
      TexOutput("</H3><P>\n\n");
      if (DocumentDate)
      {
        TexOutput("<H3>");
        TraverseChildrenFromChunk(DocumentDate);
        TexOutput("</H3><P>\n\n");
      }
      TexOutput("\n</CENTER>\n");
      TexOutput("<P><HR><P>\n");
    }
    break;
  }
  case ltHELPREF:
  case ltHELPREFN:
  case ltPOPREF:
  case ltURLREF:
  {
    if (start)
    {
      helpRefFilename = NULL;
      helpRefText = NULL;
    }
    break;
  }
  case ltBIBLIOGRAPHY:
  {
    if (start)
    {
      DefaultOnMacro(macroId, no_args, start);
    }
    else
    {
      DefaultOnMacro(macroId, no_args, start);
      TexOutput("</DL>\n");
    }
    break;
  }
  case ltHRULE:
  {
    if (start)
    {
      TexOutput("<HR>\n");
    }
    break;
  }
  case ltRULE:
  {
    if (start)
    {
      TexOutput("<HR>\n");
    }
    break;
  }
  case ltTABLEOFCONTENTS:
  {
    if (start)
    {
      FILE *fd = fopen(ContentsName, "r");
      if (fd)
      {
        int ch = getc(fd);
        while (ch != EOF)
        {
          putc(ch, Titlepage);
          ch = getc(fd);
        }
        fclose(fd);
      }
      else
      {
        TexOutput("RUN TEX2RTF AGAIN FOR CONTENTS PAGE\n");
        OnInform("Run Tex2RTF again to include contents page.");
      }
    }
    break;
  }
  case ltLANGLEBRA:
  {
    if (start)
      TexOutput("&lt;");
    break;
  }
  case ltRANGLEBRA:
  {
    if (start)
      TexOutput("&gt;");
    break;
  }
  case ltQUOTE:
  case ltQUOTATION:
  {
    if (start)
      TexOutput("<BLOCKQUOTE>");
    else
      TexOutput("</BLOCKQUOTE>");
    break;
  }
  case ltCAPTION:
  case ltCAPTIONSTAR:
  {
    if (start)
    {
      figureNo ++;

      char figBuf[40];
      if (DocumentStyle != LATEX_ARTICLE)
        sprintf(figBuf, "Figure %d.%d: ", chapterNo, figureNo);
      else
        sprintf(figBuf, "Figure %d: ", figureNo);

      TexOutput(figBuf);
    }
    else
    {
      char *topicName = FindTopicName(GetNextChunk());

      AddTexRef(topicName, NULL, NULL,
           ((DocumentStyle != LATEX_ARTICLE) ? chapterNo : figureNo),
            ((DocumentStyle != LATEX_ARTICLE) ? figureNo : 0));
    }
    break;
  }
  default:
    DefaultOnMacro(macroId, no_args, start);
    break;
  }
}

// Called on start/end of argument examination
Bool HTMLOnArgument(int macroId, int arg_no, Bool start)
{
  switch (macroId)
  {
  case ltCHAPTER:
  case ltCHAPTERSTAR:
  case ltCHAPTERHEADING:
  case ltSECTION:
  case ltSECTIONSTAR:
  case ltSECTIONHEADING:
  case ltSUBSECTION:
  case ltSUBSECTIONSTAR:
  case ltSUBSUBSECTION:
  case ltSUBSUBSECTIONSTAR:
  case ltGLOSS:
  case ltMEMBERSECTION:
  case ltFUNCTIONSECTION:
  {
    if (!start && (arg_no == 1))
      currentSection = GetArgChunk();
    return FALSE;
    break;
  }
  case ltFUNC:
  {
    if (start && (arg_no == 1))
      TexOutput("<B>");

    if (!start && (arg_no == 1))
      TexOutput("</B> ");

    if (start && (arg_no == 2))
    {
      if (!suppressNameDecoration) TexOutput("<B>");
      currentMember = GetArgChunk();
    }
    if (!start && (arg_no == 2))
    {
      if (!suppressNameDecoration) TexOutput("</B>");
    }
    
    if (start && (arg_no == 3))
      TexOutput("(");
    if (!start && (arg_no == 3))
     TexOutput(")");
   break;
  }
  case ltCLIPSFUNC:
  {
    if (start && (arg_no == 1))
      TexOutput("<B>");
    if (!start && (arg_no == 1))
      TexOutput("</B> ");

    if (start && (arg_no == 2))
    {
      if (!suppressNameDecoration) TexOutput("( ");
      currentMember = GetArgChunk();
    }
    if (!start && (arg_no == 2))
    {
    }

    if (!start && (arg_no == 3))
     TexOutput(")");
    break;
  }
  case ltPFUNC:
  {
    if (!start && (arg_no == 1))
      TexOutput(" ");

    if (start && (arg_no == 2))
      TexOutput("(*");
    if (!start && (arg_no == 2))
      TexOutput(")");

    if (start && (arg_no == 2))
      currentMember = GetArgChunk();

    if (start && (arg_no == 3))
      TexOutput("(");
    if (!start && (arg_no == 3))
      TexOutput(")");
    break;
  }
  case ltPARAM:
  {
    if (start && (arg_no == 1))
      TexOutput("<B>");
    if (!start && (arg_no == 1))
      TexOutput("</B>");
    if (start && (arg_no == 2))
    {
      TexOutput("<I>");
    }
    if (!start && (arg_no == 2))
    {
      TexOutput("</I>");
    }
    break;
  }
  case ltCPARAM:
  {
    if (start && (arg_no == 1))
      TexOutput("<B>");
    if (!start && (arg_no == 1))
      TexOutput("</B> ");  // This is the difference from param - one space!
    if (start && (arg_no == 2))
    {
      TexOutput("<I>");
    }
    if (!start && (arg_no == 2))
    {
      TexOutput("</I>");
    }
    break;
  }
  case ltMEMBER:
  {
    if (!start && (arg_no == 1))
      TexOutput(" ");

    if (start && (arg_no == 2))
      currentMember = GetArgChunk();
    break;
  }
  case ltREF:
  {
    if (start)
    {
      char *sec = NULL;
      
      char *refName = GetArgData();
      if (refName)
      {
        TexRef *texRef = FindReference(refName);
        if (texRef)
        {
          sec = texRef->sectionNumber;
        }
      }
      if (sec)
      {
        TexOutput(sec);
      }
      return FALSE;
    }
    break;
  }
  case ltURLREF:
  {
    if (IsArgOptional())
      return FALSE;
    else if ((GetNoArgs() - arg_no) == 1)
    {
      if (start)
        helpRefText = GetArgChunk();
      return FALSE;
    }
    else if ((GetNoArgs() - arg_no) == 0) // Arg = 2, or 3 if first is optional
    {
      if (start)
      {
        TexChunk *ref = GetArgChunk();
        TexOutput("<A HREF=\"");
        inVerbatim = TRUE;
        TraverseChildrenFromChunk(ref);
        inVerbatim = FALSE;
        TexOutput("\">");
        if (helpRefText)
          TraverseChildrenFromChunk(helpRefText);
        TexOutput("</A>");
      }
      return FALSE;
    }
    break;
  }
  case ltHELPREF:
  case ltHELPREFN:
  case ltPOPREF:
  {
    if (IsArgOptional())
    {
      if (start)
        helpRefFilename = GetArgChunk();
      return FALSE;
    }
    if ((GetNoArgs() - arg_no) == 1)
    {
      if (start)
        helpRefText = GetArgChunk();
      return FALSE;
    }
    else if ((GetNoArgs() - arg_no) == 0) // Arg = 2, or 3 if first is optional
    {
      if (start)
      {
        char *refName = GetArgData();
        char *refFilename = NULL;

        if (refName)
        {
          TexRef *texRef = FindReference(refName);
          if (texRef)
          {
            if (texRef->refFile && strcmp(texRef->refFile, "??") != 0)
              refFilename = texRef->refFile;

            TexOutput("<A HREF=\"");
            // If a filename is supplied, use it, otherwise try to
            // use the filename associated with the reference (from this document).
            if (helpRefFilename)
	    {
              TraverseChildrenFromChunk(helpRefFilename);
              TexOutput("#");
	    }
            else if (refFilename)
	    {
              TexOutput(refFilename);
              TexOutput("#");
	    }
            TexOutput(refName);
            TexOutput("\">");
            if (helpRefText)
              TraverseChildrenFromChunk(helpRefText);
            TexOutput("</A>");
          }
        }
        else TexOutput("??");
      }
      return FALSE;
    }
    break;
  }
  case ltIMAGE:
  case ltPSBOXTO:
  {
    if (arg_no == 2)
    {
      if (start)
      {
        // Try to find an XBM or GIF image first.
        char *filename = copystring(GetArgData());
        char buf[500];
        
        strcpy(buf, filename);
        StripExtension(buf);
        strcat(buf, ".xbm");
        char *f = TexPathList.FindValidPath(buf);

        if (!f) // Try for a GIF instead
        {
          strcpy(buf, filename);
          StripExtension(buf);
          strcat(buf, ".gif");
          f = TexPathList.FindValidPath(buf);
        }
        if (f)
        {
          char *inlineFilename = copystring(f);
          char *originalFilename = TexPathList.FindValidPath(filename);
          
          // If we have found the existing filename, make the inline
          // image point to the original file (could be PS, for example)
          if (originalFilename && (strcmp(inlineFilename, originalFilename) != 0))
	  {
            TexOutput("<A HREF=\"");
            TexOutput(originalFilename);
            TexOutput("\">");
//            TexOutput("<img align=top src=\"");
            TexOutput("<img src=\"");
            TexOutput(inlineFilename);
            TexOutput("\"></A>");
	  }
          else
	  {
//            TexOutput("<img align=top src=\"");
            TexOutput("<img src=\"");
            TexOutput(inlineFilename);
            TexOutput("\">");
	  }
        }
        else
        {
          // Last resort - a link to a PS file.
          TexOutput("<A HREF=\"");
          TexOutput(filename);
          TexOutput("\">Picture</A>\n");
          sprintf(buf, "Warning: could not find an inline XBM/GIF for %s.", filename);
          OnInform(buf);
        }
      }
    }
    return FALSE;
    break;
  }
  // First arg is PSBOX spec (ignored), second is image file, third is map name.
  case ltIMAGEMAP:
  {
    static char *imageFile = NULL;
    if (start && (arg_no == 2))
    {
      // Try to find an XBM or GIF image first.
      char *filename = copystring(GetArgData());
      char buf[500];
        
      strcpy(buf, filename);
      StripExtension(buf);
      strcat(buf, ".xbm");
      char *f = TexPathList.FindValidPath(buf);

      if (!f) // Try for a GIF instead
      {
        strcpy(buf, filename);
        StripExtension(buf);
        strcat(buf, ".gif");
        f = TexPathList.FindValidPath(buf);
      }
      if (!f)
      {
        char buf[300];
        sprintf(buf, "Warning: could not find an inline XBM/GIF for %s.", filename);
        OnInform(buf);
      }
      delete[] filename;
      if (imageFile)
        delete[] imageFile;
      imageFile = NULL;
      if (f)
      {
        imageFile = copystring(f);
      }
    }
    else if (start && (arg_no == 3))
    {
      if (imageFile)
      {
        // First, try to find a .shg (segmented hypergraphics file)
        // that we can convert to a map file
        char buf[256];
        strcpy(buf, imageFile);
        StripExtension(buf);
        strcat(buf, ".shg");
        char *f = TexPathList.FindValidPath(buf);

        if (f)
        {
          f = copystring(f);
          // The default HTML file to go to is THIS file (so a no-op)
          SHGToMap(f, currentFileName);
          delete[] f;
        }

        char *mapName = GetArgData();
        TexOutput("<A HREF=\"/cgi-bin/imagemap/");
        if (mapName)
          TexOutput(mapName);
        else
          TexOutput("unknown");
        TexOutput("\">");
        TexOutput("<img src=\"");
        TexOutput(imageFile);
        TexOutput("\" ISMAP></A><P>");
        delete[] imageFile;
        imageFile = NULL;
      }
    }
    return FALSE;
    break;
  }
  case ltITEM:
  {
    if (start)
    {
      descriptionItemArg = GetArgChunk();
      return FALSE;
    }
  }
  case ltTWOCOLITEM:
  case ltTWOCOLITEMRULED:
  {
    if (start && (arg_no == 1))
      TexOutput("\n<DT> ");
    if (start && (arg_no == 2))
      TexOutput("<DD> ");
    return TRUE;
    break;
  }
  case ltNUMBEREDBIBITEM:
  {
    if (arg_no == 1 && start)
    {
      TexOutput("\n<DT> ");
    }
    if (arg_no == 2 && !start)
      TexOutput("<P>\n");
    break;
  }
  case ltBIBITEM:
  {
    char buf[100];
    if (arg_no == 1 && start)
    {
      char *citeKey = GetArgData();
      TexRef *ref = (TexRef *)TexReferences.Get(citeKey);
      if (ref)
      {
        if (ref->sectionNumber) delete[] ref->sectionNumber;
        sprintf(buf, "[%d]", citeCount);
        ref->sectionNumber = copystring(buf);
      }

      sprintf(buf, "\n<DT> [%d] ", citeCount);
      TexOutput(buf);
      citeCount ++;
      return FALSE;
    }
    if (arg_no == 2 && !start)
      TexOutput("<P>\n");
    return TRUE;
    break;
  }
  case ltMARGINPAR:
  case ltMARGINPARODD:
  case ltMARGINPAREVEN:
  case ltNORMALBOX:
  case ltNORMALBOXD:
  {
    if (start)
    {
      TexOutput("<HR>\n");
      return TRUE;
    }
    else
      TexOutput("<HR><P>\n");
    break;
  }
  /*
   * Accents
   *
   */
  case ltACCENT_GRAVE:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case 'a':
           TexOutput("&agrave;");
           break;
          case 'e':
           TexOutput("&egrave;");
           break;
          case 'i':
           TexOutput("&igrave;");
           break;
          case 'o':
           TexOutput("&ograve;");
           break;
          case 'u':
           TexOutput("&ugrave;");
           break;
          case 'A':
           TexOutput("&Agrave;");
           break;
          case 'E':
           TexOutput("&Egrave;");
           break;
          case 'I':
           TexOutput("&Igrave;");
           break;
          case 'O':
           TexOutput("&Ograve;");
           break;
          case 'U':
           TexOutput("&Igrave;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltACCENT_ACUTE:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case 'a':
           TexOutput("&aacute;");
           break;
          case 'e':
           TexOutput("&eacute;");
           break;
          case 'i':
           TexOutput("&iacute;");
           break;
          case 'o':
           TexOutput("&oacute;");
           break;
          case 'u':
           TexOutput("&uacute;");
           break;
          case 'y':
           TexOutput("&yacute;");
           break;
          case 'A':
           TexOutput("&Aacute;");
           break;
          case 'E':
           TexOutput("&Eacute;");
           break;
          case 'I':
           TexOutput("&Iacute;");
           break;
          case 'O':
           TexOutput("&Oacute;");
           break;
          case 'U':
           TexOutput("&Uacute;");
           break;
          case 'Y':
           TexOutput("&Yacute;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltACCENT_CARET:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case 'a':
           TexOutput("&acirc;");
           break;
          case 'e':
           TexOutput("&ecirc;");
           break;
          case 'i':
           TexOutput("&icirc;");
           break;
          case 'o':
           TexOutput("&ocirc;");
           break;
          case 'u':
           TexOutput("&ucirc;");
           break;
          case 'A':
           TexOutput("&Acirc;");
           break;
          case 'E':
           TexOutput("&Ecirc;");
           break;
          case 'I':
           TexOutput("&Icirc;");
           break;
          case 'O':
           TexOutput("&Ocirc;");
           break;
          case 'U':
           TexOutput("&Icirc;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltACCENT_TILDE:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case ' ':
           TexOutput("~");
           break;
          case 'a':
           TexOutput("&atilde;");
           break;
          case 'n':
           TexOutput("&ntilde;");
           break;
          case 'o':
           TexOutput("&otilde;");
           break;
          case 'A':
           TexOutput("&Atilde;");
           break;
          case 'N':
           TexOutput("&Ntilde;");
           break;
          case 'O':
           TexOutput("&Otilde;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltACCENT_UMLAUT:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case 'a':
           TexOutput("&auml;");
           break;
          case 'e':
           TexOutput("&euml;");
           break;
          case 'i':
           TexOutput("&iuml;");
           break;
          case 'o':
           TexOutput("&ouml;");
           break;
          case 'u':
           TexOutput("&uuml;");
           break;
          case 'y':
           TexOutput("&yuml;");
           break;
          case 'A':
           TexOutput("&Auml;");
           break;
          case 'E':
           TexOutput("&Euml;");
           break;
          case 'I':
           TexOutput("&Iuml;");
           break;
          case 'O':
           TexOutput("&Ouml;");
           break;
          case 'U':
           TexOutput("&Uuml;");
           break;
          case 'Y':
           TexOutput("&Yuml;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltACCENT_DOT:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case 'a':
           TexOutput("&aring;");
           break;
          case 'A':
           TexOutput("&Aring;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltACCENT_CADILLA:
  {
    if (start)
    {
      char *val = GetArgData();
      if (val)
      {
        switch (val[0])
        {
          case 'c':
           TexOutput("&ccedil;");
           break;
          case 'C':
           TexOutput("&Ccedil;");
           break;
          default:
           break;
        }
      }
    }
    return FALSE;
    break;
  }
  case ltTHEBIBLIOGRAPHY:
  {
    if (start && (arg_no == 1))
    {
      ReopenFile(&Chapters, &ChaptersName);
      AddTexRef("bibliography", ChaptersName, "bibliography");
      SetCurrentSubsectionName("bibliography", ChaptersName);

      citeCount = 1;

      SetCurrentOutput(Chapters);
      fprintf(Chapters, "<A NAME=\"%s\">\n<H2>%s", "bibliography", ReferencesNameString);

      char titleBuf[150];
      if (truncateFilenames)
        sprintf(titleBuf, "%s.htm", FileNameFromPath(FileRoot));
      else
        sprintf(titleBuf, "%s_contents.html", FileNameFromPath(FileRoot));
      AddBrowseButtons("contents", titleBuf, // Up
                       lastTopic, lastFileName,  // Last topic
                       "bibliography", ChaptersName); // This topic

      TexOutput("<head><title>");
      TexOutput(ReferencesNameString);
      TexOutput("</title></head>\n");

      SetCurrentOutputs(Contents, Chapters);
      fprintf(Contents, "\n<LI><A HREF=\"%s#%s\">", ChaptersName, "bibliography");

      fprintf(Contents, "%s</A>\n", ReferencesNameString);
      fprintf(Chapters, "</H2>\n</A>\n");

      SetCurrentOutput(Chapters);
      return FALSE;
    }
    if (!start && (arg_no == 2))
    {
    }
    return TRUE;
    break;
  }
  default:
    return DefaultOnArgument(macroId, arg_no, start);
    break;
  }
  return TRUE;
}

Bool HTMLGo(void)
{
  fileId = 0;
  inVerbatim = FALSE;
  indentLevel = 0;

  if (InputFile && OutputFile)
  {
    // Do some HTML-specific transformations on all the strings,
    // recursively
    Text2HTML(GetTopLevelChunk());

    char buf[300];
    if (truncateFilenames)
      sprintf(buf, "%s.htm", FileRoot);
    else
      sprintf(buf, "%s_contents.html", FileRoot);
    if (TitlepageName) delete[] TitlepageName;
    TitlepageName = copystring(buf);
    Titlepage = fopen(buf, "w");
    fprintf(Titlepage, "<HTML>\n");

    Contents = fopen(TmpContentsName, "w");
    if (!Titlepage || !Contents)
    {
      OnError("Cannot open output file!");
      return FALSE;
    }
    AddTexRef("contents", FileNameFromPath(TitlepageName), ContentsNameString);

    fprintf(Contents, "<P><P><H2>%s</H2><P><P>\n", ContentsNameString);

    fprintf(Contents, "<UL>\n");

    SetCurrentOutput(Titlepage);
    OnInform("Converting...");

    TraverseDocument();

    if (DocumentTitle)
    {
      SetCurrentOutput(Titlepage);
      TexOutput("\n<head><title>");
      TraverseChildrenFromChunk(DocumentTitle);
      TexOutput("</title></head>\n");
    }
    else
    {
      if (contentsString)
        fprintf(Titlepage, "<head><title>%s</title></head>\n\n", contentsString);
      else
        fprintf(Titlepage, "<head><title>%s</title></head>\n\n", FileNameFromPath(FileRoot));
    }

    fprintf(Contents, "</UL>\n\n");
    if (Titlepage)
    {
      fprintf(Titlepage, "\n</HTML>\n");
      fclose(Titlepage);
    }
    if (Contents)
    {
      fprintf(Titlepage, "\n</HTML>\n");
      fclose(Contents);
    }
    if (Chapters)
    {
      fprintf(Chapters, "\n</HTML>\n");
      fclose(Chapters);
    }
    if (Sections)
    {
      fprintf(Sections, "\n</HTML>\n");
      fclose(Sections);
    }
    if (Subsections)
    {
      fprintf(Subsections, "\n</HTML>\n");
      fclose(Subsections);
    }
    if (Subsubsections)
    {
      fprintf(Subsubsections, "\n</HTML>\n");
      fclose(Subsubsections);
    }

    if (lastFileName) delete[] lastFileName;
    lastFileName = NULL;
    if (lastTopic) delete[] lastTopic;
    lastTopic = NULL;

    if (FileExists(ContentsName)) wxRemoveFile(ContentsName);
    wxRenameFile(TmpContentsName, ContentsName);

    return TRUE;
  }
  return FALSE;
}

