/*
   Copyright 2007 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.
*/

#include "typeutils.h"
#include "cppduchain/cpptypes.h"
#include <language/duchain/ducontext.h>
#include <language/duchain/forwarddeclaration.h>
#include <language/duchain/classfunctiondeclaration.h>
#include "classdeclaration.h"

namespace TypeUtils {
  using namespace KDevelop;

  AbstractType::Ptr realType(const AbstractType::Ptr& _base, const TopDUContext* /*topContext*/, bool* constant) {

    if(constant)
      *constant = false;
    AbstractType::Ptr base = _base;
    ReferenceType::Ptr ref = base.cast<ReferenceType>();

    while( ref ) {
      if( constant )
        (*constant) |= (ref->modifiers() & AbstractType::ConstModifier);
      base = ref->baseType();
      ref = base.cast<ReferenceType>();
    }

    return base;
  }

  bool isPointerType(const AbstractType::Ptr& type) {
    return realType(type, 0).cast<PointerType>();
  }

  bool isReferenceType(const AbstractType::Ptr& type) {
    return type.cast<ReferenceType>();
  }

  bool isConstant( const AbstractType::Ptr& t ) {
    return t->modifiers() & AbstractType::ConstModifier;
  }

  bool isNullType( const AbstractType::Ptr& t ) {
    ConstantIntegralType::Ptr integral = t.cast<ConstantIntegralType>();
    if( integral && integral->dataType() == IntegralType::TypeInt && integral->value<qint64>() == 0 )
      return true;
    else
      return false;
  }

    const int unsignedIntConversionRank = 4;

  int integerConversionRank( const IntegralType::Ptr& type ) {
    /**
     * Ranks:
     * 1 - bool
     * 2 - 1 byte, char
     * 3 - 2 byte,  short int, wchar_t, unsigned short int
     * 4 - 4 byte,  int, unsigned int
     * 5 - 4 byte,  long int
     * 6 - 4 byte, long long int
     **/
    switch( type->dataType() ) {
      case IntegralType::TypeBoolean:
        return 1;
      break;
      case IntegralType::TypeChar:
        return 2;
      break;
      case IntegralType::TypeWchar_t:
        return 3;
      break;
      case IntegralType::TypeInt:
        if( type->modifiers() & AbstractType::ShortModifier )
          return 3;
        if( type->modifiers() & AbstractType::LongModifier )
          return 5;
        if( type->modifiers() & AbstractType::LongLongModifier )
          return 6;

        return 4; //default-integer
      //All other types have no integer-conversion-rank
      default:
        return 0;
    };
  }
  bool isIntegerType( const IntegralType::Ptr& type ) {
    return integerConversionRank(type) != 0; //integerConversionRank returns 0 for non-integer types
  }

  bool isFloatingPointType( const IntegralType::Ptr& type ) {
    return type->dataType() == IntegralType::TypeFloat || type->dataType() == IntegralType::TypeDouble;
  }

  bool isVoidType( const AbstractType::Ptr& type ) {
    IntegralType::Ptr integral = type.cast<IntegralType>();
    if( !integral ) return false;
    return integral->dataType() == IntegralType::TypeVoid;
  }

  ///Returns whether base is a base-class of c
  void getMemberFunctions(const CppClassType::Ptr& klass, const TopDUContext* topContext, QHash<KDevelop::FunctionType::Ptr, ClassFunctionDeclaration*>& functions, const QString& functionName, bool mustBeConstant)  {
    Declaration* klassDecl = klass->declaration(topContext);
    Cpp::ClassDeclaration* cppClassDecl = dynamic_cast<Cpp::ClassDeclaration*>(klassDecl);
    DUContext* context = klassDecl ? klassDecl->internalContext() : 0;

    int functionCount = functions.size();

    if( context ) {
      QList<Declaration*> declarations = context->findLocalDeclarations(Identifier(functionName), SimpleCursor::invalid(), topContext);
      for( QList<Declaration*>::iterator it = declarations.begin(); it != declarations.end(); ++it ) {
        KDevelop::FunctionType::Ptr function = (*it)->abstractType().cast<KDevelop::FunctionType>();
        ClassFunctionDeclaration* functionDeclaration = dynamic_cast<ClassFunctionDeclaration*>( *it );
        if( function && functionDeclaration ) {
          if( !functions.contains(function) && (!mustBeConstant || (function->modifiers() & AbstractType::ConstModifier)) ) {
            functions[function] =  functionDeclaration;
          }
        }
      }
    }

    ///One overloaded function of a specific name overloads all inherited with the same name. Think about it in the context where getMemberFunctions is used.
    if( functionCount != functions.size() )
      return;

    if(cppClassDecl) {
      //equivalent to using the imported parent-contexts
      FOREACH_FUNCTION(const Cpp::BaseClassInstance& base, cppClassDecl->baseClasses) {
        if( base.access != KDevelop::Declaration::Private ) { //we need const-cast here because the constant list makes also the pointers constant, which is not intended
          CppClassType::Ptr baseClass = base.baseClass.type().cast<CppClassType>();
          if( baseClass )
            getMemberFunctions( baseClass, topContext, functions, functionName,   mustBeConstant);
        }
      }
    }
  }

  void getMemberFunctions(const CppClassType::Ptr& klass, const TopDUContext* topContext, QList<Declaration*>& functions, const QString& functionName, bool mustBeConstant)  {
    QHash<KDevelop::FunctionType::Ptr, ClassFunctionDeclaration*> tempFunctions;
    getMemberFunctions( klass, topContext, tempFunctions, functionName, mustBeConstant );
    for( QHash<KDevelop::FunctionType::Ptr, ClassFunctionDeclaration*>::const_iterator it = tempFunctions.begin(); it != tempFunctions.end(); ++it )
      functions << (*it);
  }

  void getConstructors(const CppClassType::Ptr& klass, const TopDUContext* topContext, QList<Declaration*>& functions) {
    Declaration* klassDecl = klass->declaration(topContext);
    DUContext* context = klassDecl ? klassDecl->internalContext() : 0;
    if( !context || !context->owner() || !context->owner() ) {
      kDebug(9007) << "Tried to get constructors of a class without context";
      return;
    }
    
    Identifier id(context->owner()->identifier());
    id.clearTemplateIdentifiers();

    QList<Declaration*> declarations = context->findLocalDeclarations(id, SimpleCursor::invalid(), topContext, AbstractType::Ptr(), DUContext::OnlyFunctions);

    for( QList<Declaration*>::iterator it = declarations.begin(); it != declarations.end(); ++it ) {
      ClassFunctionDeclaration* functionDeclaration = dynamic_cast<ClassFunctionDeclaration*>( *it );
      if( functionDeclaration && functionDeclaration->isConstructor() )
        functions <<  *it;
    }
  }
  bool isPublicBaseClass( const CppClassType::Ptr& c, const CppClassType::Ptr& base, const KDevelop::TopDUContext* topContext, int* baseConversionLevels ) {
    Cpp::ClassDeclaration* fromDecl = dynamic_cast<Cpp::ClassDeclaration*>(c->declaration(topContext));
    Cpp::ClassDeclaration* toDecl = dynamic_cast<Cpp::ClassDeclaration*>(base->declaration(topContext));
    if(fromDecl && toDecl)
      return fromDecl->isPublicBaseClass(toDecl, topContext, baseConversionLevels);
    else
      return false;
  }


}
