/**
 * \file math.cpp
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author André Pönitz
 *
 * Full author contact details are available in file CREDITS.
 */

// {[(

#include <config.h>

#include "Preamble.h"
#include "tex2lyx.h"

#include "support/convert.h"

#include <iostream>

using namespace std;

namespace lyx {


bool is_math_env(string const & name)
{
	return known_math_environments.find(name) != known_math_environments.end();
}


bool is_display_math_env(string const & name)
{
	CommandMap::const_iterator it = known_math_environments.find(name);
	if (it != known_math_environments.end())
		if (!it->second.empty())
			return it->second.back() == displaymath;
	return false;
}


void parse_math(Parser & p, ostream & os, unsigned flags, const mode_type mode)
{
	while (p.good()) {
		Token const & t = p.get_token();

		debug_message("t: " + t.asInput() + " flags: " + convert<string>(flags));

		if (flags & FLAG_ITEM) {
			if (t.cat() == catSpace)
				continue;

			flags &= ~FLAG_ITEM;
			if (t.cat() == catBegin) {
				// skip the brace and collect everything to the next matching
				// closing brace
				flags |= FLAG_BRACE_LAST;
				continue;
			}

			// handle only this single token, leave the loop if done
			flags |= FLAG_LEAVE;
		}


		//
		// cat codes
		//
		if (t.cat() == catMath) {
			if (mode == MATHTEXT_MODE) {
				// we are inside some text mode thing, so opening new math is allowed
				Token const & n = p.get_token();
				if (n.cat() == catMath) {
					// TeX's $$...$$ syntax for displayed math
					os << "\\[";
					parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
					os << "\\]";
					p.get_token(); // skip the second '$' token
				} else {
					// simple $...$  stuff
					p.putback();
					os << '$';
					parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
					os << '$';
				}
			}

			else if (flags & FLAG_SIMPLE) {
				// this is the end of the formula
				return;
			}

			else {
				warning_message("\nmode: " + std::to_string(mode));
				p.error("something strange in the parser\n");
				break;
			}
		}

		else if (t.cat() == catLetter ||
			       t.cat() == catSuper ||
			       t.cat() == catSub ||
			       t.cat() == catOther ||
			       t.cat() == catAlign ||
			       t.cat() == catActive ||
			       t.cat() == catParameter)
			os << t.cs();

		else if (t.cat() == catBegin) {
			os << '{';
			parse_math(p, os, FLAG_BRACE_LAST, mode);
			os << '}';
		}

		else if (t.cat() == catEnd) {
			if (flags & FLAG_BRACE_LAST)
				return;
			os << "unexpected '}' in math\n";
		}

		else if (t.cat() == catComment) {
			if (!t.cs().empty())
				os << t.asInput();
			else
				// "%\n" combination
				p.skip_spaces();
		}

		//
		// control sequences
		//

		else if (t.cs() == "(") {
			os << "\\(";
			parse_math(p, os, FLAG_SIMPLE2, MATH_MODE);
			os << "\\)";
		}

		else if (t.cs() == "[") {
			// special handling of a few common SW user quirks
			p.skip_spaces();
			//if (p.next_token().cs() ==
			os << "\\[";
			parse_math(p, os, FLAG_EQUATION, MATH_MODE);
			os << "\\]";
		}

		else if (t.cs() == "protect")
			// ignore \\protect, will hopefully be re-added during output
			;

		else if (t.cs() == "begin") {
			string const name = p.getArg('{', '}');
			active_environments.push_back(name);
			os << "\\begin{" << name << "}";
			if (name == "tabular")
				parse_math(p, os, FLAG_END, MATHTEXT_MODE);
			else
				parse_math(p, os, FLAG_END, mode);
			os << "\\end{" << name << "}";
			active_environments.pop_back();
		}

		else if (t.cs() == "end") {
			if (flags & FLAG_END) {
				// eat environment name
				string const name = p.getArg('{', '}');
				if (name != active_environment())
					p.error("\\end{" + name + "} does not match \\begin{"
						+ active_environment() + "}");
				return;
			}
			p.error("found 'end' unexpectedly");
		}

		else if (t.cs() == ")") {
			if (flags & FLAG_SIMPLE2)
				return;
			p.error("found '\\)' unexpectedly");
		}

		else if (t.cs() == "]") {
			if (flags & FLAG_EQUATION)
				return;
			p.error("found '\\]' unexpectedly");
		}

		else if (t.cs() == "textrm" || t.cs() == "textsf" || t.cs() == "textbf"
				|| t.cs() == "texttt" || t.cs() == "textsc"
				|| t.cs() == "text" || t.cs() == "intertext") {
			os << '\\' << t.cs() << '{';
			parse_math(p, os, FLAG_ITEM, MATHTEXT_MODE);
			os << '}';
		}

		else if (t.cs() == "tag") {
			os << '\\' << t.cs();
			if (p.next_token().asInput() == "*") {
				p.get_token();
				os << '*';
			}
			os << '{';
			parse_math(p, os, FLAG_ITEM, MATHTEXT_MODE);
			os << '}';
		}

		else if (t.cs() == "mbox" || t.cs() == "fbox") {
			os << "\\" << t.cs() << '{';
			parse_math(p, os, FLAG_ITEM, MATHTEXT_MODE);
			os << '}';
		}

		else if (t.cs() == "\"") {
			string const name = p.verbatim_item();
			LYXERR0("name: " << name);
			if (name == "a")
				os << "ä";
			else if (name == "o")
				os << "ö";
			else if (name == "u")
				os << "ü";
			else if (name == "A")
				os << "Ä";
			else if (name == "O")
				os << "Ö";
			else if (name == "U")
				os << "Ü";
			else
				os << "\"{" << name << "}";
		}

		else if (t.cs() == "ss")
			os << "ß";

		else if (t.cs() == "cr") {
			// lyx can't handle \\cr
			warning_message("Converting TeX '\\cr' to LaTeX '\\\\'.");
			os << "\\\\";
		}

		else if (t.cs() == "vref" || t.cs() == "vpageref") {
			os << t.asInput();
			preamble.registerAutomaticallyLoadedPackage("varioref");
		}

		else if (t.cs() == "textipa") {
			os << t.asInput();
			preamble.registerAutomaticallyLoadedPackage("tipa");
		}

		else
			os << t.asInput();

		if (flags & FLAG_LEAVE) {
			flags &= ~FLAG_LEAVE;
			break;
		}
	}
}




// }])


} // namespace lyx
