/********************************************************************************************************
 * PROGRAM	  : 
 * DATE - TIME  : lundi 10 avril 2006 - 22:28
 * AUTHOR	   : IComplete Team and Anacr0x ( fred.julian at gmail.com )
 * FILENAME	 : 
 * LICENSE	  : GPL
 * COMMENTARY   : Modified file (with qt) of the icomplete project
 ********************************************************************************************************/

#include <QRegExp>
#include <QFile>
#include <QProcess>
#include <QDebug>

#include "parse.h"
#include "tree.h"

#define QD qDebug() << __FILE__ << __LINE__ << ":"
extern QString simplifiedText( QString );

char* Parse::scanForIdent(const char **expr)
{
	static char ident[256];
	int valid_chars = 0;	/* number of identified characters in the ident string */

	char c;
	while ((c = **expr) != '\0')
	{
		if (valid_chars == 0 && isspace(c))
		{
			(*expr)++;
			continue;
		}
		// XXX: Namespace support
		else if (isalpha(c) || c == '_')	// ???: || c == ':')
		{
			ident[valid_chars] = c;
			if (++valid_chars == 255)
			{
				ident[255] = '\0';
				return ident;
			}
		}
		/* a digit may be part of an ident but not from the start */
		else if (isdigit(c))
		{
			if (valid_chars)
			{
				ident[valid_chars] = c;
				if (++valid_chars == 255)
				{
					ident[255] = '\0';
					return ident;
				}
			}
			else
				return NULL;

		}
		else
			break;

		(*expr)++;
	}

	if (valid_chars)
	{
		ident[valid_chars] = '\0';
		return ident;
	}
	else
		return NULL;
}

bool Parse::scanForFuncdef(const QString &expr)
{
	const char *pExpr = expr.toAscii();
	char c;
	while ((c = *pExpr) != '\0')
	{
		switch (c)
		{
		case ' ':
		case '\t':
		case '\n':
			pExpr++;
			continue;

		case '(':
			return true;
		default:
			return false;
		}
	}
	return false;
}

QString Parse::getTypeOfToken(const QString &ident, const QString &className,
                              Scope * scope, bool token_is_function)
{

	/* if we have a variable and already found a local definition, just return it after duplicating */
	if (!token_is_function && scope->localdef.length() && ident!="this")
		return scope->localdef;

	/* if the identifier is this-> return the current class */
	if (ident == "this")
	{
		return scope->scope;
	}
	Tree *tree = NULL;
	if (className.length())
	{
		tree = Tree::buildInheritanceTree(className);
		if (!tree)
			return NULL;
	}

	tagFileInfo info;
	tagEntry entry;
	tagFile *tfile = tagsOpen(tagsFilePath.toAscii(), &info);
	if (tfile && info.status.opened)
	{
		if (tagsFind(tfile, &entry, ident.toAscii(), TAG_OBSERVECASE | TAG_FULLMATCH) ==
		        TagSuccess)
		{
			do
			{
				if (tree && !tree->isMemberOfScope(&entry, scope))
					continue;

				const char *kind = tagsField(&entry, "kind");
				if (token_is_function)	/* only list if tag is a function */
				{
					if (!kind
					        || (strcmp(kind, "function") && strcmp(kind, "prototype")))
						continue;
				}
				else		/* or a variable */
				{
					//brc: add externvar for extern variables like cout
					if (!kind
					        || (strcmp(kind, "variable") && strcmp(kind, "externvar")
					            //brc: namespace workarround: add namespace
					            && strcmp(kind, "namespace") && strcmp(kind, "member")))
						continue;
				}

				/* need to duplicate the pattern, don't ask me why */
				QString type = extractTypeQualifier(entry.address.pattern, ident);
				if(tree)
					tree->freeTree();
				tagsClose(tfile);
				return type;
			}
			while (tagsFindNext(tfile, &entry) == TagSuccess);
		}
		tagsClose(tfile);
	}
	return NULL;
}

QString Parse::extractTypeQualifier(const QString &str, const QString &varName)
{
#define STRING      "\".*\""
#define BRACKETEXPR "\\{.*\\}"
//#define IDENT       "[a-zA-Z_][a-zA-Z0-9_]*"
//#define IDENT       "[a-zA-Z_:][a-zA-Z0-9_:]*"
#define IDENT       "[a-zA-Z_:][a-zA-Z0-9_:]*"
#define WS          "[ \t\r\n]*"
#define PTR         "[\\*&]?\\*?"
#define INITIALIZER "=(" WS IDENT WS ")|=(" WS STRING WS ")|=(" WS BRACKETEXPR WS ")"
#define CONSTRUCTOR "(\\(" WS IDENT WS "\\))|(\\(" WS STRING WS "\\))"
#define ARRAY       WS "\\[" WS "[0-9]*" WS "\\]" WS
	QString pattern = "(" IDENT ")"				// the 'std' in example a)
	                  "(::" IDENT ")*"			// ::vector
	                  "(" WS "<[^>;]*>)?[ \t\n*&]{1}"	// <char *>
	                  "(" WS PTR WS IDENT WS "(" ARRAY ")*" WS "((" INITIALIZER ")?|(" CONSTRUCTOR ")?)" WS "," WS ")*" // other variables for the same ident (string i,j,k;)
	                  "(" WS "[*&])?";			// check again for pointer/reference type

	QRegExp rx(pattern + WS + varName + "[^.-\\w]");

	if( varName.isEmpty() )
		return QString();
	QString text = simplifiedText( str );
	int pos = -1;
	int begin = -1;
	while( (pos = text.lastIndexOf(varName, begin) ) != -1 )
	{
		begin = pos;
		while( begin>0 && text.at(begin)!= '\n' )
			begin--;
		int end = pos;
		while( end<text.length()-1 && text.at(end)!= '\n' )
			end++;
		QString line = text.mid(begin, end-begin).simplified();
		if( !line.isEmpty() )
		{
			int pos2 = 0;
			while ((pos2 = rx.indexIn(line, pos2)) != -1)
			{
				if( !QString("|delete|else|endif|throw|return|").contains( "|"+rx.cap(1)+"|" ) && rx.cap(1)!=":")
				{
					return rx.cap(1);
				}
				pos2 += rx.matchedLength();
			}
		}
	}
	return QString();
}

bool Parse::getTypeOfExpression(const QString &expr, Expression * exp, Scope * scope)
{
	char *ident = NULL;

	/* very basic stack implementation to keep track of tokens */
	const int max_items = 20;
	const char *stack[max_items];
	int num_stack = 0;

	/* skip nested brackets */
	int brackets = 0, square_brackets = 0;

	bool in_ident = false;	/* if the current position is within an identifier */
	bool extract_ident = false;	/* if we extract the next string which looks like an identifier - only after: . -> and ( */

	QByteArray array(expr.toLocal8Bit() ); 
	if( !array.count() ) 
		return false; 
	unsigned long len = array.length(); 
	const char *first = array.data(), *start = first + len;

	while (--start >= first && start < (first + len) ) 
	{
		/* skip brackets */
		if (brackets > 0 || square_brackets > 0)
		{
			if (*start == '(')
				--brackets;
			else if (*start == ')')
				++brackets;
			else if (*start == '[')
				--square_brackets;
			else if (*start == ']')
				++square_brackets;
			continue;
		}
		/* identifier */
		if (isdigit(*start))
			in_ident = false;
		else if (isalpha(*start) || *start == '_')
			in_ident = true;
		else
		{
			switch (*start)
			{
				/* skip whitespace */
			case ' ':
			case '\t':
				if (in_ident)
					goto extract;
				else
				{
					in_ident = false;
					continue;
				}

				/* continue searching to the left, if we
				 * have a . or -> accessor */
			case '.':
				if (in_ident && extract_ident)
				{
					const char *ident = start + 1;
					if (num_stack < max_items)
						stack[num_stack++] = ident;
					else
						return false;
				}
				in_ident = false;
				extract_ident = true;
				continue;
			case '>':		/* pointer access */
			case ':':		/* static access */
				if ((*start == '>' && (start - 1 >= first && *(start - 1) == '-'))
				        || (*start == ':'
				            && (start - 1 >= first && *(start - 1) == ':')))
				{
					
					if (in_ident && extract_ident)
					{
						const char *ident = start + 1;
						if (num_stack < max_items)
							stack[num_stack++] = ident;
						else
							return false;
					}
					in_ident = false;
					extract_ident = true;
					--start;
					continue;
				}
				else
				{
					start++;
					goto extract;
				}
			case '(':		/* start of a function */
				if (extract_ident)
				{
					start++;
					goto extract;
				}
				else
				{
					extract_ident = true;
					in_ident = false;
					break;
				}

			case ')':
				if (in_ident)	/* probably a cast - (const char*)str */
				{
					start++;
					goto extract;
				}
				brackets++;
				break;

			case ']':
				if (in_ident)
				{
					start++;
					goto extract;
				}
				square_brackets++;
				break;

			default:
				start++;
				goto extract;
			}
		}
	}

extract:
	/* ident is the leftmost token, stack[*] the ones to the right
	 * Example: str.left(2,5).toUpper()
	 *          ^^^ ^^^^      ^^^^^^^
	 *        ident stack[1]  stack[0]
	 */
	ident = scanForIdent(&start);
	if (!ident)
		return false;

	/* we need this little kludge with old_type to avoid mem leaks */
	QString type, old_type;

	/* for static access, parsing is done, just return the identifier */
	if (exp->access != AccessStatic)
	{
		QString saved_start = start;	// start can change, so we save it
		getScopeAndLocals(scope, expr, ident);
		/* if we have the start of a function/method, don't return the type
		 * of this function, but className, which it is member of */
		type = getTypeOfToken(ident, NULL, scope, scanForFuncdef(saved_start));
		/* members can't be local variables */
		scope->localdef[0] = '\0';
		while (type!="" && num_stack > 0)
		{
			ident = scanForIdent(&stack[--num_stack]);
			old_type = type;
			//<brc code>: namespace workaround: if the "type" of an identifier is namespace ignore it
			if (type == "namespace")
				old_type = "";
			//<\end brc code>
			type = getTypeOfToken(ident, old_type.toAscii(), scope,
			                      scanForFuncdef(stack[num_stack]));
		}
	}
	else			/* static member */
		type = ident;
	/* copy result into passed Expression argument */
	if (type!="" && !(exp->access == AccessInFunction))
	{
		exp->className = type;
		exp->function[0] = '\0';
		return true;
	}
	else if (exp->access == AccessInFunction)
	{
		exp->className = old_type;
		exp->function = ident;
		return true;
	}
	return false;
}

Expression Parse::parseExpression(QString expression, Scope * scope, bool showAllResults)
{
	Expression exp;
	exp.access = ParseError;

	int len = expression.length();
	if (len < 1)
		return exp;

	// we found that the user has beginning to write
	if(!showAllResults)
	{
        QByteArray array(expression.toLocal8Bit() ); 
        if( !array.count() ) 
        	return exp; 
        const char *start = array.data(), *p = start;
		//const char *start = expression.toLocal8Bit(), *p = start;
		while (*p)
			p++;
		p--;
		while (p != start && *p != '.' && *p != '\n' && *p != '\r' && *p != ' ' && *p != '\t' && *p != ';' && *p != '{' && *p != '}' && *p != '(' && *p != ',' && (p-1) != start && (*p != '>' || *(p - 1) != '-') && (*p != ':' || *(p - 1) != ':'))
			p--;
		exp.writing = p+1;
	}
	/* A global function should be completed, if nothing else is found */
	exp.access = AccessGlobal;

	bool allow_AccessInFunction = true;	// false, when other characters than whitespace were typed
	/* search for the type of the correct completion */
	while (--len >= 0)
	{
		char last = ((const char*)expression.toAscii())[len];
		switch (last)
		{
		case ' ':
		case '\t':
			/* skip initial spaces */
			if (exp.access == AccessGlobal)
			{
				/*                    exp.access = ParseError;*/
				continue;
			}
			else
			{
				exp.access = AccessGlobal;
				goto extract;
			}

		case '>':
			if (len && expression[len - 1] == '-')
			{
				exp.access = AccessPointer;
				expression[len + 1] = '\0';	/* truncate the string */
				goto extract;
			}
			else
			{
				exp.access = AccessGlobal;	/* XXX: AccessError ? */
				goto extract;
			}
		case '.':
			exp.access = AccessMembers;
			expression[len + 1] = '\0';	/* truncate the string */
			goto extract;
		case ':':
			if (len && expression[len - 1] == ':')
			{
				exp.access = AccessStatic;
				expression[len + 1] = '\0';	/* truncate the string */
				goto extract;
			}
			else
			{
				exp.access = AccessGlobal;
				goto extract;
			}
		case '(':
			if (allow_AccessInFunction)
			{
				exp.access = AccessInFunction;
				expression[len + 1] = '\0';	/* truncate the string */
			}
			goto extract;

		default:
			if (isalpha(last) || last == '_')
			{
				/* we only list function definitions if after the opening
				 * ( bracket of the function is no identifier */
				allow_AccessInFunction = false;
				break;
			}
			else
				goto extract;
		}
	}

extract:
	/* now extract the type out of the string */
	if (exp.access == AccessMembers || exp.access == AccessPointer
	        || exp.access == AccessStatic || exp.access == AccessInFunction)
	{
		if (!getTypeOfExpression(expression.toAscii(), &exp, scope))
			exp.access = ParseError;
	}
	else if (exp.access == AccessGlobal)
		getScopeAndLocals(scope, expression.toAscii(), NULL);

	return exp;
}

Tag Parse::prettifyTag(const tagEntry * entry)
{
	Tag tag;
	tag.name = entry->name;
	tag.kind = tagsField(entry, "kind");
	tag.access = tagsField(entry, "access");
	tag.signature = tagsField(entry, "signature");
	tag.isFunction = (tag.signature.length());

	char *p = (char *) entry->address.pattern;

	/* for a macro the pattern is already parsed */
	if (tag.kind == "macro")
	{
		/* NOTE: exuberant-ctags 5.5.4 does not provide a valid pattern for found macros
		 * work around it, by getting the line myself */
		char pat_macro[512];
		unsigned long line = entry->address.lineNumber;
		if (line == 0)		/* sometimes ctags can't find the correct line */
			return tag;

		FILE *fileMacro = fopen(entry->file, "r");
		if (fileMacro)
		{
			while ((p = fgets(pat_macro, 512, fileMacro)) != NULL)
			{
				line--;
				if (line <= 0)
				{
					/* remove leading spaces */
					p++;	/* skip over # - it is added later again */
					while (*p == ' ' || *p == '\t')
						p++;

					tag.longName = '#' + p;
					/* remove new line at the end */
					tag.longName.remove(tag.longName.length() - 1, 1);
					break;
				}
			}
			tag.longName += " [macro]";
			fclose(fileMacro);
			return tag;
		}
	}
	/* special handling for enumerator */
	if (tag.kind == "enumerator")
	{
		/* skip whitespace from variable/function patterns */
		size_t skip = strspn(p, "/^ \t");
		p += skip;
		/* remove trailing $/ characters */
		char *pos = NULL;
		if ((pos = strstr(p, "$/")) != NULL)
			*pos = '\0';
		/* replace \/\/ and \/ * *\/ to correctly show comments */
		while ((pos = strstr(p, "\\/\\/")) != NULL)
			memcpy(pos, "  //", 4);
		while ((pos = strstr(p, "\\/*")) != NULL)
			memcpy(pos, " /*", 3);
		while ((pos = strstr(p, "*\\/")) != NULL)
			memcpy(pos, " */", 3);

		tag.longName += QString(p) + " [enumerator]";
		return tag;
	}

	size_t skip = strspn(p, "/^ \t");
	p += skip;
	/* remove trailing $/ characters */
	char *pos = NULL;
	if ((pos = strstr(p, "$/")) != NULL)
		*pos = '\0';

	tag.longName += p;

	/* if it is a function, add signature as well */
	if (tag.signature.length())
		tag.parameters += tag.signature;

	if (tag.access.length() && tag.access == "private" || tag.access == "protected")
		tag.longName += '[' + tag.access + ']';

	return tag;
}


bool Parse::getScopeAndLocals(Scope * sc, const QString &expr, const QString &ident)
{
	// initialize scope if nothing better is found
	sc->scope = "";
	sc->localdef = "";

	/* create a tags file for `expr' with function names only.
	 * The function with the highest line number is our valid scope
	 * --sort=no, because tags generation probably faster, and
	 * so I just have to look for the last entry to find 'my' tag
	 */

	QString command =
	    ctagsCmdPath +
	    " --language-force=c++ --sort=no --fields=fKmnsz --c++-kinds=fn -o \"" +
	    smallTagsFilePath + "\" \"" + parsedFilePath + '\"';
	// I don't process any user input, so system() should be safe enough
	QProcess ctags;
	ctags.execute(command);
	QFile::remove (parsedFilePath);
	if (ctags.exitStatus() == QProcess::CrashExit)
		return false;

	/* find the last entry, this is our current scope */
	tagFileInfo info;
	tagFile *tfile = tagsOpen(smallTagsFilePath.toAscii(), &info);
	tagEntry entry;
	const char *scope = NULL;
	if (tfile && info.status.opened)
	{
		if (tagsFirst(tfile, &entry) == TagSuccess)
		{
			do
				scope = tagsField(&entry, "class");
			while (tagsNext(tfile, &entry) == TagSuccess);
		}
		tagsClose(tfile);
	}

	/* must be before the 'type = extract_type_qualifier()' code, which modifies scope */
	if (scope)
		sc->scope = scope;

	/* get local definition (if any) */
	if (ident!="")
	{
		QString type = extractTypeQualifier(expr, ident);
		if (type.length())
		{
			sc->localdef = type;
		}
		else
			sc->localdef = "";
	}

	QFile::remove (smallTagsFilePath); 
	
	return true;
}
