#ifndef SYNTAXCHECK_H
#define SYNTAXCHECK_H

#include "mostQtHeaders.h"
#include "smallUsefulFunctions.h"
#include "qdocumentline_p.h"
#include <QThread>
#include <QSemaphore>
#include <QMutex>
#include <QQueue>

class LatexDocument;

class Environment
{
public:
	Environment(): id(-1), excessCol(0), dlh(0), ticket(0) {}

	QString name;
	int id;
	int excessCol;
	QDocumentLineHandle *dlh;
	int ticket;

	bool operator ==(const Environment &env) const
	{
		return (name == env.name) && (id == env.id) && (excessCol == env.excessCol);
	}
	bool operator !=(const Environment &env) const
	{
		return (name != env.name) || (id != env.id) || (excessCol != env.excessCol);
	}
};

typedef QStack<Environment> StackEnvironment;

Q_DECLARE_METATYPE(StackEnvironment)

class SyntaxCheck : public SafeThread
{
	Q_OBJECT

public:
	enum ErrorType {
		ERR_none,
		ERR_unrecognizedCommand,
		ERR_unrecognizedMathCommand,
		ERR_unrecognizedTabularCommand,
		ERR_TabularCommandOutsideTab,
		ERR_MathCommandOutsideMath,
		ERR_TabbingCommandOutside,
		ERR_tooManyCols,
		ERR_tooLittleCols,
		ERR_missingEndOfLine,
		ERR_closingUnopendEnv,
		ERR_EnvNotClosed,
		ERR_unrecognizedKey,
		ERR_unrecognizedKeyValues
	};

	struct SyntaxLine {
		StackEnvironment prevEnv;
		TokenStack stack;
		int ticket;
		bool clearOverlay;
		QDocumentLineHandle *dlh;
	};

	struct Error {
		QPair<int, int> range;
		ErrorType type;
	};

	typedef QList<Error > Ranges;

	explicit SyntaxCheck(QObject *parent = 0);

	void putLine(QDocumentLineHandle *dlh, StackEnvironment previous, TokenStack stack, bool clearOverlay = false);
	void stop();
	void setErrFormat(int errFormat);
	QString getErrorAt(QDocumentLineHandle *dlh, int pos, StackEnvironment previous, TokenStack stack);
	int verbatimFormat;
	void waitForQueueProcess();
	static int containsEnv(const LatexParser &parser, const QString &name, const StackEnvironment &envs, const int id = -1);
	int topEnv(const QString &name, const StackEnvironment &envs, const int id = -1);
	bool checkCommand(const QString &cmd, const StackEnvironment &envs);
	static bool equalEnvStack(StackEnvironment env1, StackEnvironment env2);
	bool queuedLines();
	void setLtxCommands(const LatexParser &cmds);
	void markUnclosedEnv(Environment env);

signals:
	void checkNextLine(QDocumentLineHandle *dlh, bool clearOverlay, int ticket);

protected:
	void run();
	void checkLine(const QString &line, Ranges &newRanges, StackEnvironment &activeEnv, QDocumentLineHandle *dlh, int ticket);
	void checkLine(const QString &line, Ranges &newRanges, StackEnvironment &activeEnv, QDocumentLineHandle *dlh, TokenList tl, TokenStack stack, int ticket);

private:
	QQueue<SyntaxLine> mLines;
	QSemaphore mLinesAvailable;
	QMutex mLinesLock;
	bool stopped;
	int syntaxErrorFormat;
	LatexParser *ltxCommands;

	LatexParser newLtxCommands;
	bool newLtxCommandsAvailable;
	QMutex mLtxCommandLock;
	bool stackContainsDefinition(const TokenStack &stack) const;
};

#endif // SYNTAXCHECK_H
