/*
* This file is part of Code::Blocks Studio, an open-source cross-platform IDE
* Copyright (C) 2003  Yiannis An. Mandravellos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Contact e-mail: Yiannis An. Mandravellos <mandrav@codeblocks.org>
* Program URL   : http://www.codeblocks.org
*
* $Id: parserthread.cpp,v 1.22.2.1 2005/10/25 07:59:01 mandrav Exp $
* $Date: 2005/10/25 07:59:01 $
*/

#include <sdk.h>
#include "parserthread.h"
#include <wx/app.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <globals.h>

#include <cctype>

int THREAD_START = wxNewId();
int THREAD_END = wxNewId();
int NEW_TOKEN = wxNewId();
int FILE_NEEDS_PARSING = wxNewId();

ParserThread::ParserThread(wxEvtHandler* parent,bool* abortflag,
							const wxString& bufferOrFilename,
							bool isLocal,
							ParserThreadOptions& options,
							TokensArray* tokens)
	: m_pParent(parent),
	m_pTokens(tokens),
	m_pLastParent(0L),
	m_IsLocal(isLocal),
	m_StartBlockIndex(0),
	m_Options(options)
{
//	m_pAbort=abortflag;
	//ctor
	m_Tokens.m_Options.wantPreprocessor = options.wantPreprocessor;

	if (!bufferOrFilename.IsEmpty())
	{
		if (!options.useBuffer)
		{
			m_Filename = bufferOrFilename;
			m_Tokens.Init(m_Filename);
		}
		else
			m_Tokens.InitFromBuffer(bufferOrFilename);
	}
	m_LastScope = tsUndefined;
}

ParserThread::~ParserThread()
{
	//dtor
}

void ParserThread::Log(const wxString& log)
{
	wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, NEW_TOKEN);
	event.SetString(log);
	event.SetInt(m_Tokens.GetLineNumber());
	wxPostEvent(m_pParent, event);
	wxYield();
}

void ParserThread::SetTokens(TokensArray* tokens)
{
    m_pTokens = tokens;
}

void* ParserThread::DoRun()
{
//	wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, THREAD_START);
//    event.SetString(m_Filename);
//	event.SetInt((int)this);
//	wxPostEvent(m_pParent, event);
//
////    Log("ParserThread running for " + m_Filename);
//	Parse();
//
//	wxCommandEvent event1(wxEVT_COMMAND_MENU_SELECTED, THREAD_END);
//	event1.SetString(m_Filename);
//	event1.SetInt((int)this);
//	wxPostEvent(m_pParent, event1);

	return 0L;
}

wxChar ParserThread::SkipToOneOfChars(const wxString& chars, bool supportNesting)
{
	unsigned int level = m_Tokens.GetNestingLevel();
	while (1)
	{
		wxString token = m_Tokens.GetToken();
		if (token.IsEmpty())
			return '\0'; // eof

		if (!supportNesting ||
			(supportNesting && m_Tokens.GetNestingLevel() == level))
		{
			wxChar ch = token.GetChar(0);
			if (chars.Find(ch) != wxNOT_FOUND)
				return ch;
		}
	}
}

void ParserThread::SkipBlock()
{
	// skip tokens until we reach }
	// block nesting is taken into consideration too ;)

	// this is the nesting level we start at
	// we subtract 1 because we 're already inside the block
	// (since we 've read the {)
	unsigned int level = m_Tokens.GetNestingLevel() - 1;
	while (1)
	{
		wxString token = m_Tokens.GetToken();
		if (token.IsEmpty())
			break; // eof

		// if we reach the initial nesting level, we are done
		if (level == m_Tokens.GetNestingLevel())
			break;
	}
}

void ParserThread::SkipAngleBraces()
{
    int nestLvl = 0;
    while (true)
    {
        wxString tmp = m_Tokens.GetToken();
        if (tmp.Matches(_T("<")))
            ++nestLvl;
        else if (tmp.Matches(_T(">")))
            --nestLvl;
        else if (tmp.Matches(_T(";")))
        {
        	// unget token - leave ; on the stack
        	m_Tokens.UngetToken();
            break;
        }
        else if (tmp.IsEmpty())
            break;
        if (nestLvl <= 0)
            break;
    }
}

bool ParserThread::ParseBufferForFunctions(const wxString& buffer)
{
    if (!m_pTokens)
        return false;
	m_pTokens->Clear();
	m_Tokens.InitFromBuffer(buffer);
	if (!m_Tokens.IsOK())
		return false;

	m_Str.Clear();
    m_EncounteredNamespaces.Clear();

	while (1)
	{
        if (!m_pTokens || TestDestroy())
            return false;

		wxString token = m_Tokens.GetToken();
		if (token.IsEmpty())
			break;
#if 0
	if (!m_Str.IsEmpty())
		Log(m_Str);
#endif
#if 0
	if (!token.IsEmpty())
		Log(token);
#endif

		if (token.Matches(_T(";")))
		{
			m_Str.Clear();
		}
		else if (token.Matches(_T("{")))
		{
			SkipBlock();
			m_Str.Clear();
		}
		else if (token.Matches(_T("}")))
		{
			m_Str.Clear();
		}
//		else if (token.Matches("::"))
//		{
//			m_Str.Clear();
//		}
		else if (token.Matches(_T("typedef")) ||
			token.Matches(_T(":")))
		{
			SkipToOneOfChars(_T(";}"), true);
			m_Str.Clear();
		}
		else if (token.Matches(_T("extern")) ||
			token.StartsWith(_T("__asm")))
		{
			SkipToOneOfChars(_T(";"));
			//m_Str.Clear();
		}
		else if (token.Matches(_T("#")))
		{
			m_Tokens.GetToken();
			m_Tokens.GetToken();
			m_Str.Clear();
		}
		else
		{
			wxString peek = m_Tokens.PeekToken();
			if (!peek.IsEmpty())
			{
				if (peek.GetChar(0) == '(')
				{
					// function
					// ignore some well-known wxWindows macros
					if (token.Matches(_T("BEGIN_EVENT_TABLE")))
					{
						// skip till after END_EVENT_TABLE
						while (!token.IsEmpty() && !token.Matches(_T("END_EVENT_TABLE")))
							token = m_Tokens.GetToken(); // skip args
						m_Tokens.GetToken(); // skip args
					}
					else if (!token.Matches(_T("*_EVENT_TABLE")) &&
						!token.Matches(_T("IMPLEMENT_APP")) &&
						!token.Matches(_T("WX_DECLARE_*")) &&
						!token.Matches(_T("WX_DEFINE_*")))
                    {
						if (m_Str.GetChar(0) == '~')
						{
                            token = _T('~') + token;
                            m_Str.Clear();
                        }
                        HandleFunction(token);
                    }
					else
						m_Tokens.GetToken(); // skip args
				}
				else
				{
					m_Str << token << _T(" ");
				}
			}
		}
	}
	return true;
}

bool ParserThread::Parse()
{
    if (!m_pTokens)
        return false;
#if 0
	if (!m_Options.useBuffer)
		Log("Parsing " + m_Filename);
#endif

	if (!m_Tokens.IsOK())
	{
		//Log("Cannot parse " + m_Filename);
		return false;
    }

	if (m_Options.useBuffer)
		m_StartBlockIndex = m_pTokens->GetCount();
    else
        m_StartBlockIndex = 0;
	m_Str.Clear();
	m_LastToken.Clear();
    m_EncounteredNamespaces.Clear();

	while (1)
	{
		if (!m_pTokens || TestDestroy())
			break;

		wxString token = m_Tokens.GetToken();
		if (token.IsEmpty())
			break;
#if 0
	if (!m_Str.IsEmpty())
		Log(m_Str);
#endif
#if 0
	if (!token.IsEmpty())
		Log(token);
#endif

		if (token.Matches(_T(";")))
		{
			m_Str.Clear();
		}
		else if (token.Matches(_T("delete")) ||
                token.Matches(_T(".")) ||
				(token.Matches(_T(">")) && m_LastToken.Matches(_T("-"))))
		{
			m_Str.Clear();
			SkipToOneOfChars(_T(";}"));
		}
		else if (token.Matches(_T("{")))
		{
			if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
				SkipBlock();
			m_Str.Clear();
		}
		else if (token.Matches(_T("}")))
		{
			m_pLastParent = 0L;
			m_LastScope = tsUndefined;
			m_Str.Clear();
			// the only time we get to find a } is when recursively called by e.g. HandleClass
			// we have to return now...
			if (!m_Options.useBuffer)
                break;
		}
		else if (token.Matches(_T(":")))
		{
			if (m_LastToken.Matches(_T("public")))
				m_LastScope = tsPublic;
			else if (m_LastToken.Matches(_T("protected")))
				m_LastScope = tsProtected;
			else if (m_LastToken.Matches(_T("private")))
				m_LastScope = tsPrivate;
			m_Str.Clear();
		}
		else if (token.Matches(_T("while")) ||
			token.Matches(_T("if")) ||
			token.Matches(_T("do")) ||
			token.Matches(_T("else")) ||
			token.Matches(_T("for")) ||
			token.Matches(_T("switch")))
		{
			if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
				SkipToOneOfChars(_T(";}"), true);
			else
				m_Tokens.GetToken(); //skip args
			m_Str.Clear();
		}
		else if (token.Matches(_T("typedef")) ||
			token.Matches(_T("return")) ||
			token.Matches(_T(":")))
		{
			SkipToOneOfChars(_T(";}"), true);
			m_Str.Clear();
		}
		else if (token.Matches(_T("extern")))
		{
            // check for "C"
            m_Str = m_Tokens.GetToken();
            if (m_Str.Matches(_T("\"C\"")))
            {
                m_Tokens.GetToken(); // "eat" {
                Parse(); // time for recursion ;)
            }
            else
                SkipToOneOfChars(_T(";")); // skip externs
//                m_Tokens.UngetToken(); // nope, return the token back...
            m_Str.Clear();
        }
        else if (token.StartsWith(_T("__asm")))
		{
			SkipToOneOfChars(_T(";"), true);
			//m_Str.Clear();
		}
		else if (token.Matches(_T("static")) ||
			token.Matches(_T("virtual")) ||
			token.Matches(_T("inline")))
		{
			// do nothing (skip it)
			//m_Str.Clear();
		}
		else if (token.Matches(_T("#")))
		{
			token = m_Tokens.GetToken();
			if (token.Matches(_T("include")))
				HandleIncludes();
			else if (token.Matches(_T("define")))
				HandleDefines();
			m_Str.Clear();
		}
		else if (token.Matches(_T("using"))) // using namespace ?
		{
            SkipToOneOfChars(_T(";}"), true);
			m_Str.Clear();
		}
		else if (token.Matches(_T("namespace")))
		{
			m_Str.Clear();
			HandleNamespace();
		}
		else if (token.Matches(_T("template")))
		{
			m_Str.Clear();
			SkipToOneOfChars(_T(">;"), true);
		}
		else if (token.Matches(_T("class")))
		{
			m_Str.Clear();
			HandleClass();
		}
		else if (token.Matches(_T("struct")))
		{
			m_Str.Clear();
			HandleClass(false);
		}
		else if (token.Matches(_T("enum")))
		{
			m_Str.Clear();
			HandleEnum();
		}
		else if (token.Matches(_T("union")))
        {
            SkipToOneOfChars(_T("{;"));
//            if (m_Tokens.GetToken() == "{")
            {
                Token* oldparent = m_pLastParent;
                Parse();
                m_Str.Clear();
                m_pLastParent = oldparent;
            }
        }
#if 1
		else if (token.Matches(_T("operator")))
		{
			wxString func = token;
			while (1)
			{
				token = m_Tokens.GetToken();
				if (!token.IsEmpty())
				{
					if (token.GetChar(0) == '(')
					{
						// check for operator()()
						wxString peek = m_Tokens.PeekToken();
						if (!peek.IsEmpty() && peek.GetChar(0) != '(')
							m_Tokens.UngetToken();
						else
							func << token;
						break;
					}
					else
						func << token;
				}
				else
					break;
			}
			HandleFunction(func, true);
			m_Str.Clear();
		}
#endif
		else
		{
			wxString peek = m_Tokens.PeekToken();
			if (!peek.IsEmpty())
			{
				if (peek.GetChar(0) == '(' && !m_Options.useBuffer)
				{
					if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
					{
						// function
						// ignore some well-known wxWindows macros
						if (token.Matches(_T("BEGIN_EVENT_TABLE")))
						{
							// skip till after END_EVENT_TABLE
							while (!token.IsEmpty() && !token.Matches(_T("END_EVENT_TABLE")))
								token = m_Tokens.GetToken(); // skip args
							m_Tokens.GetToken(); // skip args
						}
						else if (!token.Matches(_T("*_EVENT_TABLE*")) &&
							!token.Matches(_T("IMPLEMENT_APP")) &&
							!token.Matches(_T("IMPLEMENT_DYNAMIC_CLASS")) &&
							!token.Matches(_T("WX_DECLARE_*")) &&
							!token.Matches(_T("WX_DEFINE_*")))
                        {
//                            Log("m_Str='"+m_Str+"'");
//                            Log("token='"+token+"'");
//                            Log("peek='"+peek+"'");
							HandleFunction(token);
                        }
						else
							m_Tokens.GetToken(); // skip args
					}
					else
						m_Tokens.GetToken(); // eat args when parsing block
					m_Str.Clear();
				}
				else if (peek.Matches(_T(",")))
				{
                    // example decl to encounter a comma: int x,y,z;
                    // token should hold the var (x/y/z)
                    // m_Str should hold the type (int)
                    DoAddToken(tkVariable, token);
                    // skip comma (we had peeked it)
                    m_Tokens.GetToken();
				}
				else if (peek.Matches(_T("<")))
				{
                    // a template, e.g. someclass<void>::memberfunc
                    // we have to skip <>, so we 're left with someclass::memberfunc
                    SkipAngleBraces();
                    peek = m_Tokens.PeekToken();
                    if (peek.Matches(_T("::")))
                    {
    //                    Log("peek='::', token='" + token + "', m_LastToken='" + m_LastToken + "', m_Str='" + m_Str + "'");
                        m_EncounteredNamespaces.Add(token);
                        m_Tokens.GetToken(); // eat ::
                    }
                    if (m_Str.IsEmpty())
                    {
                        m_Str = GetStringFromArray(m_EncounteredNamespaces, _T("::")) + token;
                        m_EncounteredNamespaces.Clear();
                    }
				}
                else if (peek.Matches(_T("::")))
                {
//                    Log("peek='::', token='" + token + "', m_LastToken='" + m_LastToken + "', m_Str='" + m_Str + "'");
                    m_EncounteredNamespaces.Add(token);
                    m_Tokens.GetToken(); // eat ::
                }
				else if ((peek.Matches(_T(";")) || (m_Options.useBuffer && peek.GetChar(0) == _T('(')) && !m_Str.Contains(_T("::"))) && m_pTokens)
				{
//					Log("m_Str='"+m_Str+"'");
//					Log("token='"+token+"'");
//					Log("peek='"+peek+"'");
					if (!m_Str.IsEmpty() && (isalpha(token.GetChar(0)) || token.GetChar(0) == '_'))
					{
                        DoAddToken(tkVariable, token);
                    }
					//m_Str.Clear();
				}
				else
				{
					m_Str << token << _T(" ");
				}
			}
		}
		m_LastToken = token;
	}
	return true;
}

Token* ParserThread::TokenExists(const wxString& name, Token* parent, short int kindMask)
{
    if (!m_pTokens)
        return 0;
    if (!parent)
    {
        // when parsing a block, we must make sure the token does not already exist...
        for (unsigned int i = m_StartBlockIndex; i < m_pTokens->GetCount(); ++i)
        {
            Token* token = m_pTokens->Item(i);
            if ((token->m_TokenKind & kindMask) && token->m_Name.Matches(name))
                return token;
        }
    }
    else
    {
        // search only under the parent token
        for (unsigned int i = 0; i < parent->m_Children.GetCount(); ++i)
        {
            Token* token = parent->m_Children.Item(i);
            if ((token->m_TokenKind & kindMask) && token->m_Name.Matches(name))
                return token;
        }
    }
	return 0L;
}

wxString ParserThread::GetActualTokenType()
{
    // we will compensate for spaces between
    // namespaces (e.g. NAMESPACE :: SomeType) wich is valid C++ construct
    // we 'll remove spaces that follow a semicolon
	int pos = 0;
	while (pos < (int)m_Str.Length())
	{
        if (m_Str.GetChar(pos) == ' ' &&
            (
                (pos > 0 && m_Str.GetChar(pos - 1) == ':') ||
                (pos < (int)m_Str.Length() - 1 && m_Str.GetChar(pos + 1) == ':')
            )
           )
        {
            m_Str.Remove(pos, 1);
        }
        else
            ++pos;
	}

	// m_Str contains the full text before the token's declaration
	// an example m_Str value would be: const wxString&
	// what we do here is locate the actual return value (wxString in this example)
	// it will be needed by code completion code ;)
	pos = m_Str.Length() - 1;
	// we walk m_Str backwards until we find a non-space character which also is
	// not * or &
	//                        const wxString&
	// in this example, we would stop here ^
    while (pos >= 0 &&
            (isspace(m_Str.GetChar(pos)) ||
            m_Str.GetChar(pos) == '*' ||
            m_Str.GetChar(pos) == '&'))
        --pos;
	if (pos >= 0)
	{
		// we have the end of the word we 're interested in
		int end = pos;
		// continue walking backwards until we find the start of the word
		//                               const wxString&
		// in this example, we would stop here ^
        while (pos >= 0 && (isalnum(m_Str.GetChar(pos)) || m_Str.GetChar(pos) == '_' || m_Str.GetChar(pos) == ':'))
            --pos;
		return m_Str.Mid(pos + 1, end - pos);
	}
	return wxEmptyString;
}

Token* ParserThread::DoAddToken(TokenKind kind, const wxString& name, const wxString& args, bool isOperator)
{
	wxMutexLocker lock(s_mutexProtection);
	if (m_Options.useBuffer && TokenExists(name))
		return 0L;
	Token* newToken = new Token;
	m_Str.Trim();
	if (kind == tkDestructor)
	{
		// special class destructors case
		newToken->m_Name = _T("~") + name;
		m_Str.Clear();
	}
	else
		newToken->m_Name = name;

    // check for implementation member function
    Token* localParent = 0;
    if (m_EncounteredNamespaces.GetCount())
    {
        unsigned int count = m_EncounteredNamespaces.GetCount();
        for (unsigned int i = 0; i < count; ++i)
        {
//            Log("NS: '" + m_EncounteredNamespaces[i] + "' for " + newToken->m_Name);
            localParent = TokenExists(m_EncounteredNamespaces[i], localParent, tkClass | tkNamespace);
            if (!localParent)
                break;
        }
        m_EncounteredNamespaces.Clear();
    }
    if (localParent)
    {
//        Log("Parent found for " + m_Str + " " + newToken->m_Name + ": " + localParent->m_DisplayName);
        Token* existing = TokenExists(newToken->m_Name, localParent);
        if (existing)
        {
//            Log("Existing found for " + newToken->m_Name);
            // if the token exists, all we have to do is adjust the
            // implementation file/line
            existing->m_ImplFilename = m_Tokens.GetFilename();
            existing->m_ImplLine = m_Tokens.GetLineNumber();
            delete newToken;
            return existing;
        }
    }

	newToken->m_Type = m_Str;
	newToken->m_ActualType = GetActualTokenType();
	newToken->m_Args = args;
	newToken->m_Scope = m_LastScope;
	newToken->m_TokenKind = kind;
	newToken->m_IsLocal = m_IsLocal;
	newToken->m_pParent = m_pLastParent;
	newToken->m_Filename = m_Tokens.GetFilename();
	newToken->m_Line = m_Tokens.GetLineNumber();
	newToken->m_ImplLine = 0;
	newToken->m_IsOperator = isOperator;
	newToken->m_IsTemporary = m_Options.useBuffer;
//    Log("Added token " +name+ ", type '" +newToken->m_Type+ "', actual '" +newToken->m_ActualType+ "'");
	if (m_pLastParent)
		newToken->m_DisplayName << m_pLastParent->m_Name << _T("::");
	newToken->m_DisplayName << newToken->m_Name << args;
	if (!newToken->m_Type.IsEmpty())
		newToken->m_DisplayName << _T(" : ") << newToken->m_Type;

    if (m_pTokens)
        m_pTokens->Add(newToken);
    if (m_pLastParent)
        m_pLastParent->AddChild(newToken);

	return newToken;
}

void ParserThread::HandleIncludes()
{
	wxString filename;
	bool isGlobal = !m_IsLocal;
	wxString token = m_Tokens.GetToken();
	// now token holds something like:
	// "someheader.h"
	// < and will follow iostream.h, >
	if (!token.IsEmpty())
	{
		if (token.GetChar(0) == '"')
		{
			// "someheader.h"
			token.Replace(_T("\""), _T(""));
			filename = token;
		}
		else if (token.GetChar(0) == '<')
		{
			isGlobal = true;
			// next token is filename, next is . (dot), next is extension
			// basically we 'll loop until >
			while (1)
			{
				token = m_Tokens.GetToken();
				if (token.IsEmpty())
					break;
				if (token.GetChar(0) != '>')
					filename << token;
				else
					break;
			}
		}
	}

	if (!filename.IsEmpty())
	{
		wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, FILE_NEEDS_PARSING);
		event.SetString(m_Filename + _T("+") + filename);
		// setting all #includes as global
		// it's amazing how many projects use #include "..." for global headers (MSVC mainly - booh)
		event.SetInt(1);//isGlobal);
//		wxPostEvent(m_pParent, event);

        // since we 'll be calling directly the parser's method, let's make it thread-safe
		static wxMutex lock;
		wxMutexLocker l(lock);
		m_pParent->ProcessEvent(event);
	}
}

void ParserThread::HandleDefines()
{
	wxString filename;
	wxString token = m_Tokens.GetToken();
	m_Str.Clear();
	// now token holds something like:
	// BLAH_BLAH
	if (!token.IsEmpty())
	{
        // make sure preprocessor definitions are not going under namespaces or classes!
        Token* oldParent = m_pLastParent;
        m_pLastParent = 0L;

		Token* newToken = DoAddToken(tkPreprocessor, token);
		if (newToken)
            newToken->m_Line -= 1; // preprocessor definitions need correction for the line number
		if (m_Tokens.PeekToken().GetChar(0) == '(') // TODO: find better way...
			m_Tokens.GetToken(); // eat args

        m_pLastParent = oldParent;
	}
}

void ParserThread::HandleNamespace()
{
    wxString ns = m_Tokens.GetToken();
    wxString next = m_Tokens.PeekToken();

    if (next.Matches(_T("{")))
    {
        // use the existing copy (if any)
        Token* newToken = TokenExists(ns, 0, tkNamespace);
        if (!newToken)
            newToken = DoAddToken(tkNamespace, ns);
        if (!newToken)
            return;

        m_Tokens.GetToken(); // eat {

        Token* lastParent = m_pLastParent;
        TokenScope lastScope = m_LastScope;

        m_pLastParent = newToken;
        // default scope is: public for namespaces (actually no, but emulate it)
        m_LastScope = tsPublic;

        Parse();

        m_pLastParent = lastParent;
        m_LastScope = lastScope;
    }
    else
        SkipToOneOfChars(_T(";{")); // some kind of error in code ?
}

void ParserThread::HandleClass(bool isClass)
{
    int lineNr = m_Tokens.GetLineNumber();
	wxString ancestors;
	while (1)
	{
		wxString current = m_Tokens.GetToken();
		wxString next = m_Tokens.PeekToken();
		if (!current.IsEmpty() && !next.IsEmpty())
		{
			if (next.Matches(_T("<"))) // template specialization
			{
                SkipAngleBraces();
                next = m_Tokens.PeekToken();
			}

			if (next.Matches(_T(":"))) // has ancestor(s)
			{
                //Log("Class " + current + " has ancestors");
				m_Tokens.GetToken(); // eat ":"
				while (1)
				{
					wxString tmp = m_Tokens.GetToken();
					next = m_Tokens.PeekToken();
					if (!tmp.Matches(_T("public")) &&
						!tmp.Matches(_T("protected")) &&
						!tmp.Matches(_T("private")) &&
						!tmp.Matches(_T(">")) &&
						!tmp.Matches(_T(",")))
                    {
                        // fix for namespace usage in ancestors
                        if (tmp.Matches(_T("::")) || next.Matches(_T("::")))
                            ancestors << tmp;
						else
                            ancestors << tmp << _T(',');
						//Log("Adding ancestor " + tmp);
                    }
					if (next.IsEmpty() ||
						next.Matches(_T("{")) ||
						next.Matches(_T(";")))
						break;
                    else if (next.Matches(_T("<")))
                    {
                        // template class
                        int nest = 0;
                        m_Tokens.GetToken(); // eat "<"
                        while (1)
                        {
                            wxString tmp1 = m_Tokens.GetToken();
                            if (tmp1.Matches(_T("<")))
                                ++nest;
                            else if (tmp1.Matches(_T(">")))
                                --nest;

                            if (tmp1.IsEmpty() ||
                                tmp1.Matches(_T("{")) ||
                                tmp1.Matches(_T(";")) ||
                                (tmp1.Matches(_T(">")) && nest <= 0))
                            {
                                m_Tokens.UngetToken(); // put it back before exiting
                                break;
                            }
                        }
                    }
				}
                //Log("Ancestors: " + ancestors);
			}

			if (current.Matches(_T("{"))) // unnamed class/struct
			{
				Token* lastParent = m_pLastParent;
				TokenScope lastScope = m_LastScope;

				// default scope is: private for classes, public for structs
				m_LastScope = isClass ? tsPrivate : tsPublic;

				Parse();

				m_pLastParent = lastParent;
				m_LastScope = lastScope;
                break;
			}
			else if (next.Matches(_T("{")))   // no ancestor(s)
			{
				Token* newToken = DoAddToken(tkClass, current);
				if (!newToken)
					return;
                newToken->m_Line = lineNr; // correct line number (might be messed if class has ancestors)
				newToken->m_AncestorsString = ancestors;

                m_Tokens.GetToken(); // eat {

				Token* lastParent = m_pLastParent;
				TokenScope lastScope = m_LastScope;

				m_pLastParent = newToken;
				// default scope is: private for classes, public for structs
				m_LastScope = isClass ? tsPrivate : tsPublic;

				Parse();

				m_pLastParent = lastParent;
				m_LastScope = lastScope;
                break;
			}
			else if (next.Matches(_T(";"))) // forward decl; we don't care
				break;
			else if (next.GetChar(0) == '(') // function: struct xyz& DoSomething()...
			{
				HandleFunction(current);
				break;
			}
		}
		else
			break;
	}
}

void ParserThread::HandleFunction(const wxString& name, bool isOperator)
{
    //Log("Adding function '"+name+"': m_Str='"+m_Str+"'");
	wxString args = m_Tokens.GetToken();
	if (!m_Str.StartsWith(_T("friend")))
	{
		TokenKind kind = tkFunction;
		bool CtorDtor = m_pLastParent && name.Matches(m_pLastParent->m_Name);
		if (!CtorDtor)
		{
            // check for m_EncounteredNamespaces
            unsigned int count = m_EncounteredNamespaces.GetCount();
            if (count)
            {
                Token* localParent = 0;
                for (unsigned int i = 0; i < count; ++i)
                {
                    localParent = TokenExists(m_EncounteredNamespaces[i], localParent, tkClass | tkNamespace);
                    if (!localParent)
                        break;
                }
                CtorDtor = localParent && name.Matches(localParent->m_Name);
            }
		}

		if (CtorDtor)
		{
			m_Str.Trim();
			if (m_Str.IsEmpty())
				kind = tkConstructor;
			else if (m_Str.Matches(_T("~")))
				kind = tkDestructor;
		}
//        Log("Adding function '"+name+"': m_Str='"+m_Str+"'"+", enc_ns="+(m_EncounteredNamespaces.GetCount()?m_EncounteredNamespaces[0]:"nil"));
		DoAddToken(kind, name, args, isOperator);
	}
	if (!m_Tokens.PeekToken().Matches(_T("}")))
		SkipToOneOfChars(_T(";}"), true);
}

void ParserThread::HandleEnum()
{
	// enums have the following rough definition:
	// enum [xxx] { type1 name1 [= 1][, [type2 name2 [= 2]]] };
	bool isUnnamed = false;
	wxString token = m_Tokens.GetToken();
	if (token.IsEmpty())
		return;
    else if (token.Matches(_T("{")))
	{
        // we have an un-named enum
		token = _T("Un-named");
		m_Tokens.UngetToken(); // return '{' back
		isUnnamed = true;
    }

	Token* newEnum = 0L;
	unsigned int level = 0;
	if (isalpha(token.GetChar(0)))
	{
		if (m_Tokens.PeekToken().GetChar(0) != '{')
			return;

        if (isUnnamed)
        {
            // for unnamed enums, look if we already have "Unnamed", so we don't
            // add a new one for every unnamed enum we encounter, in this scope...
            newEnum = TokenExists(token, m_pLastParent, tkEnum);
        }

        if (!newEnum) // either named or first unnamed enum
            newEnum = DoAddToken(tkEnum, token);
		level = m_Tokens.GetNestingLevel();
		m_Tokens.GetToken(); // skip {
	}
	else
	{
		if (token.GetChar(0) != '{')
			return;
		level = m_Tokens.GetNestingLevel() - 1; // we 've already entered the { block
	}

	while (1)
	{
		// process enumerators
		token = m_Tokens.GetToken();
		wxString peek = m_Tokens.PeekToken();
		if (token.IsEmpty() || peek.IsEmpty())
			return; //eof
		if (token.Matches(_T("}")) && level == m_Tokens.GetNestingLevel())
			break;
		// assignments (=xxx) are ignored by the tokenizer,
		// so we don't have to worry about them here ;)
		if (peek.Matches(_T(",")) || peek.Matches(_T("}")) || peek.Matches(_T(":")))
		{
            // this "if", avoids non-valid enumerators
            // like a comma (if no enumerators follow)
            if (isalpha(token.GetChar(0)))
            {
                Token* lastParent = m_pLastParent;
                m_pLastParent = newEnum;
                DoAddToken(tkEnumerator, token);
                m_pLastParent = lastParent;
			}
			if (peek.Matches(_T(":")))
			{
				// bit specifier (eg, xxx:1)
				//walk to , or }
				SkipToOneOfChars(_T(",}"));
			}
		}
	}
	// skip to ;
	token = m_Tokens.GetToken();
	while (!token.IsEmpty() && !token.Matches(_T(";")))
		token = m_Tokens.GetToken();
}


syntax highlighted by Code2HTML, v. 0.9.1