// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//                                                                
// 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                                            

#include "PointCutContext.h"
#include "TransformInfo.h"
#include "ACModel/Utils.h"

using namespace Puma;

bool is_derived_from (ACM_Class *base, ACM_Class *derived) {
  typedef ACM_Container<ACM_Class, false> Container;
  for (Container::iterator i = base->get_derived().begin ();
      i != base->get_derived().end (); ++i) {
    if (*i == derived || is_derived_from(*i, derived))
      return true;
  }
  return false;
}

PointCutExpr *PointCutContext::lookup_virtual_pct_func (ACM_Class *scope, ACM_Pointcut *pct_func) {
  // check whether the virtual pointcut is defined in the concrete aspect
  ACM_Name *result = map_lookup(*scope, pct_func->get_name ());
  if (result && result->type_val() == JPT_Pointcut)
    return (PointCutExpr*)((ACM_Pointcut*)result)->get_parsed_expr().get ();

  // get the scope of the virtual pointcut
  ACM_Any *fct_scope = (ACM_Any*)pct_func->get_parent();
  if (!fct_scope || !(fct_scope->type_val() & (JPT_Class|JPT_Aspect)))
    return 0;
  ACM_Class *fct_class = (ACM_Class*)fct_scope;

  // lookup the pointcut in the base classes/aspects
  typedef ACM_Container<ACM_Class, false> Container;
  for (Container::iterator i = scope->get_bases().begin ();
      i != scope->get_bases().end (); ++i) {
    if (*i != fct_class && !is_derived_from (fct_class, *i)) {
      continue;
    }
    PointCutExpr *pce = lookup_virtual_pct_func(*i, pct_func);
    if (pce) {
      return pce;
    }
  }
  return 0;
}

bool PointCutContext::is_arg_type_compatible_with_class(const ACM_Arg *arg) const {
  assert(in_that() || in_target());
#ifdef FRONTEND_PUMA
  CTypeInfo *check_type = TI_Arg::of(*arg)->type_info();
  if (check_type->isPointer () && check_type->BaseType ()->is_void ())
    return true; // "void*" matches any class!
  if ((check_type->isPointer () || check_type->isAddress ()) &&
    check_type->BaseType ()->isClass())
    return true;
#else // FRONTEND_CLANG
  clang::QualType check_type = TI_Arg::of(*arg)->type();
  if (check_type->isPointerType() && check_type->isVoidPointerType())
    return true; // "void*" matches any class!
  if ((check_type->isPointerType() || check_type->isReferenceType()) &&
    check_type->getPointeeType().getCanonicalType ()->isRecordType())
    return true;
#endif
  return false;
}

bool PointCutContext::is_arg_type_matching(const ACM_Arg *arg, ACM_Any &jpl) const {
#ifdef FRONTEND_PUMA
  // determine the type of the advice argument
  if (in_that() || in_target()) {
    CTypeInfo *check_type = TI_Arg::of(*arg)->type_info();
    if (check_type->isPointer () && check_type->BaseType ()->is_void ())
      check_type = 0; // "void*" matches any class!
    else if (check_type->isPointer () || check_type->isAddress ())
      check_type = check_type->BaseType ();
  }

  // determine the type of the joinpoint
  CTypeInfo *curr_type = 0;
  if (jpl.type_val () == JPT_Type)
    curr_type = TI_Type::of ((ACM_Type&)jpl)->type_info ();
  else if (jpl.type_val () == JPT_Class ||
           jpl.type_val () == JPT_Aspect) {
    curr_type = ((TI_Class*)((ACM_Class&)jpl).transform_info ())->class_info ()->TypeInfo ();
  }
  assert (curr_type);
  if (!curr_type)
    return false;

  // check if the current type matches
  if (_check_type && *_check_type != *curr_type) {
    return false;
  }
#else // FRONTEND_CLANG
  // determine the type of the advice argument
  clang::QualType check_type = TI_Arg::of(*arg)->type();
  if (in_that() || in_target()) {
    if (check_type->isPointerType() && check_type->isVoidPointerType())
      check_type = clang::QualType(); // "void*" matches any class!
    else if ((check_type->isPointerType() || check_type->isReferenceType()) &&
      check_type->getPointeeType().getCanonicalType ()->isRecordType())
      check_type = check_type->getPointeeType().getCanonicalType ();
  }

  // determine the type of the joinpoint
  clang::QualType curr_type;
  if (jpl.type_val () == JPT_Type)
    curr_type = TI_Type::of ((ACM_Type&)jpl)->type ();
  else if (jpl.type_val () == JPT_Class ||
           jpl.type_val () == JPT_Aspect) {
    curr_type =
        clang::QualType(((TI_Class *)((ACM_Class &)jpl).transform_info())
                            ->decl()->getTypeForDecl(), 0);
  }
  assert (!curr_type.isNull());
  if (curr_type.isNull())
    return false;
    
  // check if the current type matches
  if (!check_type.isNull() &&
      check_type.getCanonicalType () != curr_type.getCanonicalType ()) {
    return false;
  }
#endif
  return true;
}
