/*
 * xlputils.cc
 *
 * Utility functions for helping convert Latex files
 * into XLP files suitable for compiling into wxHelp
 * files.
 *
 * Julian Smart September 1993
 *
 */

#include <wx.h>
#include "tex2any.h"
#include "tex2rtf.h"
#include <ctype.h>

long currentBlockId = -1;
static TexChunk *descriptionItemArg = NULL;
static int indentLevel = 0;
static int noColumns = 0;
static int currentTab = 0;
static Bool tableVerticalLineLeft = FALSE;
static Bool tableVerticalLineRight = FALSE;
static Bool inTable = FALSE;
wxList hyperLinks(wxKEY_INTEGER);
wxList hyperLabels(wxKEY_STRING);
FILE *Index = NULL;

void PadToTab(int tabPos)
{
  int currentCol = GetCurrentColumn();
  for (int i = currentCol; i < tabPos; i++)
    TexOutput(" ", TRUE);
}

static long xlpBlockId = 0;
long NewBlockId(void)
{
  return xlpBlockId ++;
}

// Called on start/end of macro examination
void XLPOnMacro(char *name, int no_args, Bool start)
{
  char buf[100];

  if (strcmp(name, "chapter") == 0 || strcmp(name, "chapter*") == 0)
  {
    if (start)
    {
      SetCurrentOutputs(Contents, Chapters);
      long id1 = NewBlockId();
      currentBlockId = NewBlockId();

      startedSections = TRUE;
      fprintf(Contents, "\\hy-%d{%ld}{", hyBLOCK_LARGE_HEADING, id1);
      fprintf(Chapters, "\n\\hy-%d{%ld}{", hyBLOCK_LARGE_VISIBLE_SECTION, currentBlockId);
      fprintf(Index, "%ld %ld\n", id1, currentBlockId);
    }
    else
    {
      fprintf(Contents, "}\n\n");
      fprintf(Chapters, "}");
      SetCurrentOutput(Chapters);
      char *topicName = FindTopicName(GetNextChunk());
      hyperLabels.Append(topicName, (wxObject *)currentBlockId);
    }
  }
  else if (strcmp(name, "section") == 0 || strcmp(name, "section*") == 0)
  {
    if (start)
    {
      SetCurrentOutputs(Chapters, Sections);
      long id1 = NewBlockId();
      currentBlockId = NewBlockId();

      startedSections = TRUE;

      if (DocumentStyle == LATEX_ARTICLE)
        fprintf(Contents, "\\hy-%d{%ld}{", hyBLOCK_LARGE_HEADING, id1);
      else
        fprintf(Chapters, "\\hy-%d{%ld}{", hyBLOCK_BOLD, id1);
      fprintf(Sections, "\n\\hy-%d{%ld}{", hyBLOCK_LARGE_VISIBLE_SECTION, currentBlockId);
      fprintf(Index, "%ld %ld\n", id1, currentBlockId);
    }
    else
    {
      if (DocumentStyle == LATEX_ARTICLE)
        fprintf(Contents, "}\n\n");
      else
        fprintf(Chapters, "}\n\n");
      fprintf(Sections, "}");
      SetCurrentOutput(Sections);
      char *topicName = FindTopicName(GetNextChunk());
      hyperLabels.Append(topicName, (wxObject *)currentBlockId);
    }
  }
  else if (strcmp(name, "references") == 0)
  {
    FILE *jumpTo;
    if (DocumentStyle == LATEX_ARTICLE)
      jumpTo = Sections;
    else
      jumpTo = Chapters;
    if (start)
    {
      SetCurrentOutputs(Contents, jumpTo);
      long id1 = NewBlockId();
      currentBlockId = NewBlockId();

      startedSections = TRUE;
      fprintf(Contents, "\\hy-%d{%ld}{References}\n\n", hyBLOCK_LARGE_HEADING, id1);
      fprintf(jumpTo, "\n\\hy-%d{%ld}{References}", hyBLOCK_LARGE_VISIBLE_SECTION, currentBlockId);
      fprintf(Index, "%ld %ld\n", id1, currentBlockId);

      SetCurrentOutput(Chapters);
      char *topicName = "references";
      hyperLabels.Append(topicName, (wxObject *)currentBlockId);
    }
  }
  else if (strcmp(name, "subsection") == 0 || strcmp(name, "subsection*") == 0 ||
           strcmp(name, "membersection") == 0 || strcmp(name, "functionsection") == 0)
  {
    if (start)
    {
      SetCurrentOutputs(Sections, Subsections);
      long id1 = NewBlockId();
      currentBlockId = NewBlockId();
      fprintf(Sections, "\\hy-%d{%ld}{", hyBLOCK_BOLD, id1);
      fprintf(Subsections, "\n\\hy-%d{%ld}{", hyBLOCK_LARGE_VISIBLE_SECTION, currentBlockId);
      fprintf(Index, "%ld %ld\n", id1, currentBlockId);
    }
    else
    {
      fprintf(Sections, "}\n\n");
      fprintf(Subsections, "}");
      SetCurrentOutput(Subsections);
      char *topicName = FindTopicName(GetNextChunk());
      hyperLabels.Append(topicName, (wxObject *)currentBlockId);
    }
  }
  else if (strcmp(name, "subsubsection") == 0)
  {
    if (start)
    {
      SetCurrentOutputs(Subsections, Subsubsections);
      long id1 = NewBlockId();
      currentBlockId = NewBlockId();
      fprintf(Subsections, "\\hy-%d{%ld}{", hyBLOCK_BOLD, id1);
      fprintf(Subsubsections, "\n\\hy-%d{%ld}{", hyBLOCK_LARGE_VISIBLE_SECTION, currentBlockId);
      fprintf(Index, "%ld %ld\n", id1, currentBlockId);
    }
    else
    {
      fprintf(Subsections, "}\n\n");
      fprintf(Subsubsections, "}");
      SetCurrentOutput(Subsubsections);
      char *topicName = FindTopicName(GetNextChunk());
      hyperLabels.Append(topicName, (wxObject *)currentBlockId);
    }
  }
  else if ((strcmp(name, "func") == 0) || (strcmp(name, "pfunc") == 0) || (strcmp(name, "member") == 0))
  {
    SetCurrentOutput(Subsections);
    if (start)
    {
      long id = NewBlockId();
      fprintf(Subsections, "\\hy-%d{%ld}{", hyBLOCK_BOLD, id);
    }
    else
      fprintf(Subsections, "}");
  }
  else if ((strcmp(name, "void") == 0) && start)
    TexOutput("void", TRUE);
  else if ((strcmp(name, "\\") == 0) && start)
    TexOutput("\n", TRUE);
  else if (strcmp(name, "par") == 0)
  {
    if (start)
      TexOutput("\n", TRUE);
  }
  else if (strcmp(name, "bf") == 0)
  {
    if (start)
    {
      char buf[100];
      long id = NewBlockId();
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_BOLD, id);
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "it") == 0)
  {
    if (start)
    {
      char buf[100];
      long id = NewBlockId();
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_ITALIC, id);
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "tt") == 0)
  {
    if (start)
    {
      long id = NewBlockId();
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_TELETYPE, id);
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "small") == 0)
  {
    if (start)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_SMALL_TEXT, NewBlockId());
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "tiny") == 0)
  {
    if (start)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_SMALL_TEXT, NewBlockId());
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "normal") == 0)
  {
    if (start)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_NORMAL, NewBlockId());
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "large") == 0)
  {
    if (start)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_SMALL_HEADING, NewBlockId());
      TexOutput(buf);
    }
    else TexOutput("}\n");
  }
  else if (strcmp(name, "LARGE") == 0)
  {
    if (start)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_LARGE_HEADING, NewBlockId());
      TexOutput(buf);
    }
    else TexOutput("}\n");
  }
  else if ((strcmp(name, "itemize") == 0) ||
           (strcmp(name, "enumerate") == 0) ||
           (strcmp(name, "description") == 0))
  {
    if (start)
    {
//      tabCount ++;

//      if (indentLevel > 0)
//        TexOutput("\\par\\par\n");
      indentLevel ++;
//      TexOutput("\\fi0\n");
      int listType;
      if (strcmp(name, "enumerate") == 0)
        listType = LATEX_ENUMERATE;
      else if (strcmp(name, "itemize") == 0)
        listType = LATEX_ITEMIZE;
      else
        listType = LATEX_DESCRIPTION;
      itemizeStack.Insert(new ItemizeStruc(listType));

//      int indentSize = (int)(indentLevel*360);
//      sprintf(buf, "\\tx%d", indentSize);
//      PushEnvironmentStyle(buf);
    }
    else
    {
      indentLevel --;
/*
      tabCount --;
      PopEnvironmentStyle();

      if (indentLevel == 0)
      {
        TexOutput("\\par\\pard\n");
        WriteEnvironmentStyles();
      }
*/
      if (itemizeStack.First())
      {
        ItemizeStruc *struc = (ItemizeStruc *)itemizeStack.First()->Data();
        delete struc;
        delete itemizeStack.First();
      }
    }
  }
  else if (strcmp(name, "item") == 0)
  {
    wxNode *node = itemizeStack.First();
    if (node)
    {
      ItemizeStruc *struc = (ItemizeStruc *)node->Data();
      if (!start)
      {
        struc->currentItem += 1;
        char indentBuf[30];

        int indentSize = (int)(indentLevel*360);

/*        
        TexOutput("\n");
        if (struc->currentItem > 1)
        {
          TexOutput("\\par\\par");
//          WriteEnvironmentStyles();
        }

        sprintf(buf, "\\tx%d\\li%d\\fi-360\n", indentSize, indentSize);
        TexOutput(buf);
*/
        switch (struc->listType)
        {
          case LATEX_ENUMERATE:
          {
            sprintf(indentBuf, "\\hy-%d{%ld}{%d.} ",
              hyBLOCK_BOLD, NewBlockId(), struc->currentItem);
            TexOutput(indentBuf);
            break;
          }
          case LATEX_ITEMIZE:
          {
            sprintf(indentBuf, "\\hy-%d{%ld}{o} ",
              hyBLOCK_BOLD, NewBlockId());
            TexOutput(indentBuf);
            break;
          }
          default:
          case LATEX_DESCRIPTION:
          {
            if (descriptionItemArg)
            {
              sprintf(indentBuf, "\\hy-%d{%ld}{",
                 hyBLOCK_BOLD, NewBlockId());
              TexOutput(indentBuf);
              TraverseChildrenFromChunk(descriptionItemArg);
              TexOutput("} ");
              descriptionItemArg = NULL;
            }
            break;
          }
	}
      }
    }
  }
  else if (strcmp(name, "maketitle") == 0)
  {
    if (start && DocumentTitle && DocumentAuthor)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_LARGE_HEADING, NewBlockId());
      TexOutput(buf);
      TraverseChildrenFromChunk(DocumentTitle);
      TexOutput("}\n\n");
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_SMALL_HEADING, NewBlockId());
      TexOutput(buf);
      TraverseChildrenFromChunk(DocumentAuthor);
      TexOutput("}\n\n");
      if (DocumentDate)
      {
        TraverseChildrenFromChunk(DocumentDate);
        TexOutput("\n");
      }
    }
  }
  else if (strcmp(name, "tableofcontents") == 0)
  {
    if (start)
    {
      FILE *fd = fopen(ContentsName, "r");
      if (fd)
      {
        char ch = getc(fd);
        while (ch != EOF)
        {
          putc(ch, Chapters);
          ch = getc(fd);
        }
        fclose(fd);
      }
      else
      {
        TexOutput("RUN TEX2RTF AGAIN FOR CONTENTS PAGE\n");
        OnInform("Run Tex2RTF again to include contents page.");
      }
    }
  }
  else if (strcmp(name, "hardy") == 0)
  {
    if (start)
      TexOutput("HARDY", TRUE);
  }
  else if (strcmp(name, "wxCLIPS") == 0)
  {
    if (start)
      TexOutput("wxCLIPS", TRUE);
  }
  else if (strcmp(name, "verbatim") == 0)
  {
    if (start)
    {
      char buf[100];
      long id = NewBlockId();
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_TELETYPE, id);
      TexOutput(buf);
    }
    else TexOutput("}");
  }
  else if (strcmp(name, "hrule") == 0)
  {
    if (start)
    {
      TexOutput("\n------------------------------------------------------------------", TRUE);
    }
  }
  else if (strcmp(name, "hline") == 0)
  {
    if (start)
    {
      TexOutput("--------------------------------------------------------------------------------", TRUE);
    }
  }
  else if (strcmp(name, "_&") == 0)
  {
    if (start)
    {
      currentTab ++;
      int tabPos = (80/noColumns)*currentTab;
      PadToTab(tabPos);
    }
  }
  else if (strcmp(name, "tabular") == 0 || strcmp(name, "supertabular") == 0)
  {
    if (start)
    {
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_TELETYPE, NewBlockId());
      TexOutput(buf);
    }
    else
      TexOutput("}");
  }
  if (strcmp(name, "bibitem") == 0)
  {
    if (!start)
      TexOutput("\n\n", TRUE);
  }
  else DefaultOnMacro(name, no_args, start);
}

Bool XLPOnArgument(char *macro_name, int arg_no, Bool start)
{
  char buf[300];
  if (strcmp(macro_name, "func") == 0)
  {
    if (!start && (arg_no == 1))
      TexOutput(" ", TRUE);
    if (start && (arg_no == 3))
      TexOutput("(", TRUE);
    if (!start && (arg_no == 3))
     TexOutput(")", TRUE);
  }
  else if (strcmp(macro_name, "pfunc") == 0)
  {
    if (!start && (arg_no == 1))
      TexOutput(" ", TRUE);

    if (start && (arg_no == 2))
      TexOutput("(*", TRUE);
    if (!start && (arg_no == 2))
      TexOutput(")", TRUE);

    if (start && (arg_no == 3))
      TexOutput("(", TRUE);
    if (!start && (arg_no == 3))
      TexOutput(")", TRUE);
  }
  else if (strcmp(macro_name, "clipsfunc") == 0)
  {
    if (!start && (arg_no == 1))
      TexOutput(" ", TRUE);
    if (start && (arg_no == 2))
    {
      TexOutput("(", TRUE);
      long id = NewBlockId();
      sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_BOLD, id);
      TexOutput(buf);
    }
    if (!start && (arg_no == 2))
    {
      TexOutput("}");
    }
    if (!start && (arg_no == 3))
     TexOutput(")", TRUE);
  }
  else if (strcmp(macro_name, "param") == 0)
  {
    if (start && (arg_no == 2))
    {
      long id = NewBlockId();
      sprintf(buf, " \\hy-%d{%ld}{", hyBLOCK_BOLD, id);
      TexOutput(buf);
    }
    if (!start && (arg_no == 2))
    {
      TexOutput("}");
    }
  }
  else if (strcmp(macro_name, "cparam") == 0)
  {
    if (start && (arg_no == 2))
    {
      long id = NewBlockId();
      sprintf(buf, " \\hy-%d{%ld}{", hyBLOCK_BOLD, id);
      TexOutput(buf);
    }
    if (!start && (arg_no == 2))
    {
      TexOutput("}");
    }
  }
  else if (strcmp(macro_name, "member") == 0)
  {
    if (!start && (arg_no == 1))
      TexOutput(" ", TRUE);
  }
  else if (strcmp(macro_name, "label") == 0)
  {
    if (start && (arg_no == 1))
    {
      if (CurrentLabel) delete[] CurrentLabel;
      CurrentLabel = copystring(GetArgData());
    }
    return FALSE;
  }
  else if (strcmp(macro_name, "helpref") == 0 || strcmp(macro_name, "helprefn") == 0)
  {
    if (arg_no == 1)
    {
      if (start)
      {
        currentBlockId = NewBlockId();
        sprintf(buf, "\\hy-%d{%ld}{", hyBLOCK_RED_ITALIC, currentBlockId);
        TexOutput(buf);
      }
      else TexOutput("}");
    }
    if (arg_no == 2)
    {
      char *label = GetArgData();
      hyperLinks.Append(currentBlockId, (wxObject *)copystring(label));
      
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "item") == 0)
  {
    if (start && IsArgOptional())
    {
      descriptionItemArg = GetArgChunk();
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "tabular") == 0 || strcmp(macro_name, "supertabular") == 0)
  {
    if (arg_no == 1)
    {
      if (start)
      {
        inTable = TRUE;
        tableVerticalLineLeft = FALSE;
        tableVerticalLineRight = FALSE;

        char *alignString = copystring(GetArgData());

        // Count the number of columns
        noColumns = 0;
        int len = strlen(alignString);
        if (len > 0)
        {
          if (alignString[0] == '|')
            tableVerticalLineLeft = TRUE;
          if (alignString[len-1] == '|')
            tableVerticalLineRight = TRUE;
        }

        for (int i = 0; i < len; i++)
          if (isalpha(alignString[i]))
            noColumns ++;

/*
      // Experimental
      TexOutput("\\brdrt\\brdrs");
      if (tableVerticalLineLeft)
        TexOutput("\\brdrl\\brdrs");
      if (tableVerticalLineRight)
        TexOutput("\\brdrr\\brdrs");
*/

        // Calculate a rough size for each column
//        int tabPos = 80/noColumns;
        currentTab = 0;

        return FALSE;
      }
    }
    else if (arg_no == 2 && !start)
    {
      inTable = FALSE;
    }
  }
  else return DefaultOnArgument(macro_name, arg_no, start);
  return TRUE;
}

Bool XLPGo(void)
{
  xlpBlockId = 0;

  if (InputFile && OutputFile)
  {
    Contents = fopen(TmpContentsName, "w");
    Chapters = fopen("chapters.xlp", "w");
    Sections = fopen("sections.xlp", "w");
    Subsections = fopen("subsections.xlp", "w");
    Subsubsections = fopen("subsubsections.xlp", "w");
    Index = fopen("index.xlp", "w");

    // Insert invisible section marker for contents page
    fprintf(Contents, "\\hy-%d{%ld}{%s}\n",
                hyBLOCK_INVISIBLE_SECTION, NewBlockId(), "Contents\n\n");

    SetCurrentOutput(Chapters);

    fprintf(Index, "\n\\hyindex{\n\"%s\"\n",
             contentsString ? contentsString : "WXHELPCONTENTS");
    TraverseDocument();

    wxNode *node = hyperLinks.First();
    while (node)
    {
      long from = node->key.integer;
      char *label = (char *)node->Data();
      wxNode *otherNode = hyperLabels.Find(label);
      if (otherNode)
      {
        long to = (long)otherNode->Data();
        fprintf(Index, "%ld %ld\n", from, to);
      }
      node = node->Next();
    }

    fprintf(Index, "}\n");

    fclose(Contents);
    fclose(Chapters);
    fclose(Sections);
    fclose(Subsections);
    fclose(Subsubsections);
    fclose(Index);

    if (FileExists(ContentsName)) wxRemoveFile(ContentsName);
    wxRenameFile(TmpContentsName, ContentsName);

    wxConcatFiles("chapters.xlp", "sections.xlp", "tmp2.xlp");
    wxConcatFiles("tmp2.xlp", "subsections.xlp", "tmp1.xlp");
    wxConcatFiles("tmp1.xlp", "subsubsections.xlp", "tmp2.xlp");
    wxConcatFiles("tmp2.xlp", "index.xlp", OutputFile);

    wxRemoveFile("tmp1.xlp");
    wxRemoveFile("tmp2.xlp");

    wxRemoveFile("chapters.xlp");
    wxRemoveFile("sections.xlp");
    wxRemoveFile("subsections.xlp");
    wxRemoveFile("subsubsections.xlp");
    wxRemoveFile("index.xlp");
    return TRUE;
  }
  return FALSE;
}

