/* This file is part of KDevelop
    Copyright 2006 Hamish Rodda <rodda@kde.org>
    Copyright 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#ifndef DECLARATIONBUILDER_H
#define DECLARATIONBUILDER_H

#include "typebuilder.h"
#include <language/duchain/builders/abstractdeclarationbuilder.h>
#include "cppduchainexport.h"
#include <language/duchain/declaration.h>
#include <language/duchain/duchainpointer.h>
#include <language/duchain/classfunctiondeclaration.h>
#include <language/duchain/classdeclaration.h>

namespace Cpp
{
class TemplateDeclaration;
}

namespace KDevelop
{
class Declaration;
class ClassDeclaration;
class FunctionDefinition;
}

//Additional flags put into the access-policy member
enum SignalSlotFlags {
  FunctionIsSignal = 1 << 4,
  FunctionIsSlot = 1 << 5
};

typedef TypeBuilder DeclarationBuilderBase;

/**
 * A class which iterates the AST to extract definitions of types.
 */
class KDEVCPPDUCHAIN_EXPORT DeclarationBuilder: public DeclarationBuilderBase
{
public:
  DeclarationBuilder(ParseSession* session);

  /**
   * Compile either a context-definition chain, or add uses to an existing
   * chain.
   *
   * The returned context will have the TopDUContext::UpdatingContext flag set.
   *
   * \param includes contexts to reference from the top context.  The list may be changed by this function.
   */
  KDevelop::ReferencedTopDUContext buildDeclarations(Cpp::EnvironmentFilePointer file, AST *node, IncludeFileList* includes = 0, const ReferencedTopDUContext& updateContext = ReferencedTopDUContext(), bool removeOldImports = true);

  /**
   * Build.an independent du-context based on a given parent-context. Such a context may be used for expression-parsing,
   * but should be deleted as fast as possible because it keeps a reference to an independent context.
   *
   * \param url A temporary url that can be used to identify this context
   *
   * \param parent Context that will be used as parent for this context
   */
//   KDevelop::DUContext* buildSubDeclarations(const IndexedString& url, AST *node, KDevelop::DUContext* parent = 0);

  bool changeWasSignificant() const;

protected:
  virtual void visitDeclarator (DeclaratorAST*);
  virtual void visitNamespace(NamespaceAST* );
  virtual void visitClassSpecifier(ClassSpecifierAST*);
  virtual void visitBaseSpecifier(BaseSpecifierAST *node);
  virtual void visitAccessSpecifier(AccessSpecifierAST*);
  virtual void visitFunctionDeclaration(FunctionDefinitionAST*);
  virtual void visitSimpleDeclaration(SimpleDeclarationAST*);
  virtual void visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST*);
  virtual void visitParameterDeclaration(ParameterDeclarationAST* node);
  virtual void visitTypedef(TypedefAST *);
  virtual void visitTemplateParameter(TemplateParameterAST *);
  virtual void visitUsingDirective(UsingDirectiveAST *);
  virtual void visitUsing(UsingAST *);
  virtual void visitEnumSpecifier(EnumSpecifierAST*);
  virtual void visitEnumerator(EnumeratorAST* node);
  virtual void visitNamespaceAliasDefinition(NamespaceAliasDefinitionAST*);
  virtual void visitTypeId(TypeIdAST *);
  virtual void visitInitDeclarator(InitDeclaratorAST *node);
  virtual void handleRangeBasedFor(ExpressionAST* container, ForRangeDeclarationAst* iterator);
  virtual void visitAliasDeclaration(AliasDeclarationAST* );

  virtual void classTypeOpened(KDevelop::AbstractType::Ptr);
  virtual void classContextOpened(ClassSpecifierAST *node, DUContext* context);

private:
  //Returns true if the given parameter declaration clause is really a parameter declaration clause, depending on the given parameters.
  //Also collects the Qt function signature if required. In that case, true is always returned.
  bool checkParameterDeclarationClause(ParameterDeclarationClauseAST* clause);
  //Du-chain must be locked
  QualifiedIdentifier resolveNamespaceIdentifier(const QualifiedIdentifier& identifier, const KDevelop::CursorInRevision& position);

  Cpp::TemplateDeclaration* findSpecializedFrom(KDevelop::Declaration *templDecl);

  KDevelop::ForwardDeclaration* openForwardDeclaration(NameAST* name, AST* range);
  /**
   * Register a new declaration with the definition-use chain.
   * Returns the new context created by this definition.
   * @param name When this is zero, the identifier given through customName is used.
   * @param range provide a valid AST here if name is null
   * @param collapseRange If this is true, the end of the computed range will be moved to the start, so it's empty
   */
  template<class T>
  T* openDeclaration(NameAST* name, AST* range, const Identifier& customName = Identifier(), bool collapseRange = false, bool collapseRangeAtEnd = false);
  template<class T>
  T* openDeclarationReal(NameAST* name, AST* range, const Identifier& customName, bool collapseRangeAtStart, bool collapseRangeAtEnd, const RangeInRevision* customRange = 0);
  /// Same as the above, but sets it as the definition too @param forceInstance when this is true, the declaration is forced to be an instance, not a type declaration,
  /// and its assigned identified type will not get the declaration assigned.
  virtual void closeDeclaration(bool forceInstance = false);

  //Opens a Declaration that has the isDefinition flag set
  KDevelop::Declaration* openDefinition(NameAST* name, AST* range, bool collapseRange = false);
  //Opens a ClassDeclaration
  KDevelop::ClassDeclaration* openClassDefinition(NameAST* name, AST* range, bool collapseRange, KDevelop::ClassDeclarationData::ClassType classType);
  //Opens either a ClassFunctionDeclaration, a FunctionDeclaration, or a FunctionDefinition
  Declaration* openFunctionDeclaration(NameAST* name, AST* rangeNode);
  //Opens either a ClassMemberDeclaration, or a Declaration
  Declaration* openNormalDeclaration(NameAST* name, AST* rangeNode, const Identifier& customName = Identifier(), bool collapseRange = false);

  void parseStorageSpecifiers(const ListNode<uint>* storage_specifiers);
  void parseFunctionSpecifiers(const ListNode<uint>* function_specifiers);

  inline KDevelop::Declaration::AccessPolicy currentAccessPolicy() {
    if(m_accessPolicyStack.isEmpty())
      return KDevelop::Declaration::Public;
    else
      return ((KDevelop::Declaration::AccessPolicy)((m_accessPolicyStack.top() & (~((uint)FunctionIsSignal))) & (~((uint)FunctionIsSlot))));
  }
  inline void setAccessPolicy(KDevelop::Declaration::AccessPolicy policy) { m_accessPolicyStack.top() = policy; }

  Cpp::InstantiationInformation createSpecializationInformation(const Cpp::InstantiationInformation& base,
                                                                UnqualifiedNameAST* name,
                                                                KDevelop::DUContext* templateContext);
  Cpp::IndexedInstantiationInformation createSpecializationInformation(NameAST* name, DUContext* templateContext);

  void parseComments(const ListNode<uint> *comments);

  void eventuallyAssignInternalContext();
  void applyStorageSpecifiers();
  void applyFunctionSpecifiers();
  void popSpecifiers();
  void copyTemplateDefaultsFromForward(Identifier searchId, const CursorInRevision& pos);
  ///Inherit the "virtual" flag from overridden functions if it's not already present
  void inheritVirtualSpecifierFromOverridden(ClassFunctionDeclaration* classFun);
  void createFriendDeclaration(AST* range);
  void findDeclarationForDefinition(const QualifiedIdentifier &definitionSearchId);

  QStack<KDevelop::Declaration::AccessPolicy> m_accessPolicyStack;

  QStack<KDevelop::AbstractFunctionDeclaration::FunctionSpecifiers> m_functionSpecifiers;
  QStack<KDevelop::ClassMemberDeclaration::StorageSpecifiers> m_storageSpecifiers;
  QStack<uint> m_functionDefinedStack;

  bool m_changeWasSignificant, m_ignoreDeclarators;

  enum FunctionFlag {
    NoFunctionFlag,
    AbstractFunction,
    DefaultFunction,
    DeleteFunction
  };
  FunctionFlag m_functionFlag;

  //Ast Mapping members
  QStack<AST *> m_mappedNodes;

  bool m_collectQtFunctionSignature;
  QByteArray m_qtFunctionSignature;

  /// Inherited from AbstractDeclarationBuilder:

  inline Declaration* currentDeclaration() const { return m_declarationStack.isEmpty() ? 0 : m_declarationStack.top(); }
  /// Access the current declaration, casted to type \a DeclarationType. \returns the current declaration if one exists and is an instance of the given \a DeclarationType.
  template<class DeclarationType>
  inline DeclarationType* currentDeclaration() const { return m_declarationStack.isEmpty() ? 0 : dynamic_cast<DeclarationType*>(m_declarationStack.top()); }

  /// Access the current comment. \returns the current comment, or an empty string if none exists.
  inline const QByteArray& comment() const { return m_lastComment; }
  /// Set the current \a comment. \param comment the new comment.
  inline void setComment(const QByteArray& comment) { m_lastComment = comment; }
  /// Clears the current comment.
  inline void clearComment() { m_lastComment.clear(); }

private:
  QStack<Declaration*> m_declarationStack;
  Declaration* m_lastDeclaration;
  QByteArray m_lastComment;  
};

#endif // DECLARATIONBUILDER_H

