/*
 *   Copyright (c) 2000-2001, Jérôme Plût
 *   Copyright (c) 2006, Enrico Tröger
 *
 *   This source code is released for free distribution under the terms of the
 *   GNU General Public License.
 *
 *   This module contains functions for generating tags for source files
 *   for the TeX formatting system.
 */

/*
*   INCLUDE FILES
*/
#include "general.h"    /* must always come first */

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

#include "parse.h"
#include "read.h"
#include "vstring.h"
#include "routines.h"

/*
*   DATA DEFINITIONS
*/
typedef enum {
	K_COMMAND,
	K_ENVIRONMENT,
	K_SECTION,
	K_SUBSECTION,
	K_SUBSUBSECTION,
	K_CHAPTER,
	K_LABEL
} TeXKind;

static kindOption TeXKinds[] = {
	{ true, 'f', "function",      "command definitions" },
	{ true, 'c', "class",         "environment definitions" },
	{ true, 'm', "member",        "labels, sections and bibliography" },
	{ true, 'd', "macro",         "subsections" },
	{ true, 'v', "variable",      "subsubsections" },
	{ true, 'n', "namespace",     "chapters"},
	{ true, 's', "struct",        "labels and bibliography" }
};

#define TEX_BRACES (1<<0)
#define TEX_BSLASH (1<<1)
#define TEX_LABEL  (1<<2)

/*
*   FUNCTION DEFINITIONS
*/

static int getWord(const char * ref, const char **ptr)
{
	const char *p = *ptr;

	while ((*ref != '\0') && (*p != '\0') && (*ref == *p))
		ref++, p++;


	if (*ref)
		return false;

	if (*p == '*') /* to allow something like \section*{foobar} */
		p++;

	*ptr = p;
	return true;
}

static void createTag(int flags, TeXKind kind, const char * l)
{
	vString *name = vStringNew ();

	while ((*l == ' '))
		l++;
	if (flags & (TEX_BRACES | TEX_LABEL))
	{
		if (*l == '[')
		{
			while (*l != ']')
			{
				if (*l == '\0')
					goto no_tag;
				l++;
			}
			l++; /* skip the closing square bracket */
		}
		if (*l != '{')
			goto no_tag;
		l++;
	}
	if (flags & TEX_BSLASH)
	{
		if ((*(l++)) != '\\')
			goto no_tag;
	}
	if (flags & TEX_LABEL)
	{
		do
		{
			vStringPut(name, (int) *l);
			++l;
		} while ((*l != '\0') && (*l != '}'));
		if (name->buffer[0] != '}')
			makeSimpleTag(name, TeXKinds, kind);
	}
	else if (isalpha((int) *l) || *l == '@')
	{
		do
		{
			vStringPut (name, (int) *l);
			++l;
		} while (isalpha((int) *l) || *l == '@');
		makeSimpleTag(name, TeXKinds, kind);
	}
	else
	{
		vStringPut(name, (int) *l);
		makeSimpleTag(name, TeXKinds, kind);
	}

no_tag:
	vStringDelete(name);
}

static void findTeXTags(void)
{
	const char *line;

	while ((line = (const char*)readLineFromInputFile()) != NULL)
	{
		const char *cp = line;
		/*int escaped = 0;*/

		for (; *cp != '\0'; cp++)
		{
			if (*cp == '%')
				break;
			if (*cp == '\\')
			{
				cp++;

				/* \newcommand{\command} */
				if (getWord("newcommand", &cp)
					|| getWord("providecommand", &cp)
					|| getWord("renewcommand", &cp)
					)
				{
					createTag (TEX_BRACES|TEX_BSLASH, K_COMMAND, cp);
					continue;
				}

				/* \DeclareMathOperator{\command} */
				else if (getWord("DeclareMathOperator", &cp))
				{
					if (*cp == '*')
						cp++;
					createTag(TEX_BRACES|TEX_BSLASH, K_COMMAND, cp);
					continue;
				}

				/* \def\command */
				else if (getWord("def", &cp))
				{
					createTag(TEX_BSLASH, K_COMMAND, cp);
					continue;
				}

				/* \newenvironment{name} */
				else if ( getWord("newenvironment", &cp)
					|| getWord("newtheorem", &cp)
					|| getWord("begin", &cp)
					)
				{
					createTag(TEX_BRACES, K_ENVIRONMENT, cp);
					continue;
				}

				/* \bibitem[label]{key} */
				else if (getWord("bibitem", &cp))
				{
					while (*cp == ' ')
						cp++;
					if (*(cp++) != '[')
						break;
					while ((*cp != '\0') && (*cp != ']'))
						cp++;
					if (*(cp++) != ']')
						break;
					createTag(TEX_LABEL, K_LABEL, cp);
					continue;
				}

				/* \label{key} */
				else if (getWord("label", &cp))
				{
					createTag(TEX_LABEL, K_LABEL, cp);
					continue;
				}
				/* \section{key} */
				else if (getWord("section", &cp))
				{
					createTag(TEX_LABEL, K_SECTION, cp);
					continue;
				}
				/* \subsection{key} */
				else if (getWord("subsection", &cp))
				{
					createTag(TEX_LABEL, K_SUBSECTION, cp);
					continue;
				}
				/* \subsubsection{key} */
				else if (getWord("subsubsection", &cp))
				{
					createTag(TEX_LABEL, K_SUBSUBSECTION, cp);
					continue;
				}
				/* \chapter{key} */
				else if (getWord("chapter", &cp))
				{
					createTag(TEX_LABEL, K_CHAPTER, cp);
					continue;
				}
			}
		}
	}
}

extern parserDefinition* LaTeXParser (void)
{
	static const char *const extensions [] = { "tex", "sty", "idx", NULL };
	parserDefinition * def = parserNew ("LaTeX");
	def->kinds      = TeXKinds;
	def->kindCount  = ARRAY_SIZE (TeXKinds);
	def->extensions = extensions;
	def->parser     = findTeXTags;
	return def;
}
