/*
   Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
   Copyright 2014 Sven Brauch <svenbrauch@gmail.com>

   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 "referencetype.h"
#include "pointertype.h"
#include "typealiastype.h"
#include "unsuretype.h"
#include "integraltype.h"

namespace TypeUtils {
  using namespace KDevelop;

  TypePtr< KDevelop::AbstractType > unAliasedType(const TypePtr< KDevelop::AbstractType >& _type) {
    TypePtr< KDevelop::AbstractType > type = _type;
    
    TypePtr< KDevelop::TypeAliasType > alias = type.cast<KDevelop::TypeAliasType>();
    
    int depth = 0; //Prevent endless recursion
    while(alias && depth < 20) {
      
      uint hadModifiers = alias->modifiers();
      
      type = alias->type();
      
      if(hadModifiers && type)
        type->setModifiers(type->modifiers() | hadModifiers);
      
      alias = type.cast<KDevelop::TypeAliasType>();
      ++depth;
    }
    
    return type;
  }
  
  ///@todo remove constant and topContext
  AbstractType::Ptr targetType(const AbstractType::Ptr& _base, const TopDUContext* /*topContext*/, bool* /*constant*/) {

    AbstractType::Ptr base(_base);

    ReferenceType::Ptr ref = base.cast<ReferenceType>();
    PointerType::Ptr pnt = base.cast<PointerType>();
    TypeAliasType::Ptr alias = base.cast<TypeAliasType>();

    while( ref || pnt || alias ) {
      
      uint hadModifiers = base->modifiers();
      
      if( ref ) {
        base = ref->baseType();
      } else if(pnt) {
        base = pnt->baseType();
      }else{
        base = alias->type();
      }
      if((alias || ref) && hadModifiers && base)
        base->setModifiers(base->modifiers() | hadModifiers);
      
      ref = base.cast<ReferenceType>();
      pnt = base.cast<PointerType>();
      alias = base.cast<TypeAliasType>();
    }

    return base;
  }

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

    AbstractType::Ptr base(_base);

    ReferenceType::Ptr ref = base.cast<ReferenceType>();
    PointerType::Ptr pnt = base.cast<PointerType>();

    while( ref || pnt ) {
      
      if( ref ) {
        uint hadModifiers = ref->modifiers();
        base = ref->baseType();
        if(hadModifiers && base)
          base->setModifiers(base->modifiers() | hadModifiers);
      } else if(pnt) {
        base = pnt->baseType();
      }
      ref = base.cast<ReferenceType>();
      pnt = base.cast<PointerType>();
    }

    return base;
  }

  AbstractType::Ptr resolveAliasType(const AbstractType::Ptr eventualAlias)
  {
    if ( eventualAlias && eventualAlias->whichType() == KDevelop::AbstractType::TypeAlias ) {
      return eventualAlias.cast<TypeAliasType>()->type();
    }
    return eventualAlias;
  }

  bool isUsefulType(AbstractType::Ptr type)
  {
    type = resolveAliasType(type);
    if ( ! type ) {
      return false;
    }
    if ( type->whichType() != AbstractType::TypeIntegral ) {
      return true;
    }
    auto dtype = type.cast<IntegralType>()->dataType();
    if ( dtype != IntegralType::TypeMixed && dtype != IntegralType::TypeNull ) {
      return true;
    }
    return false;
  }
} // namespace TypeUtils
