// 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 "ThisJoinPoint.h"
#include "Binding.h"
#include "ACModel/Utils.h"
#include "AdviceInfo.h"
#include "TransformInfo.h"
#include "ACBase/StringUtils.h"
using namespace ACBase;

#include <sstream>
using std::stringstream;
using std::endl;

// special setup function for bindings
void ThisJoinPoint::setup (const Binding &binding) {
  if (binding.is_used()) {
    _used |= binding.has(Binding::THAT) ? THAT : 0;
    _used |= binding.has(Binding::TARGET) ? TARGET : 0;
    _used |= binding.has(Binding::RESULT) ? RESULT : 0;
    _used |= binding.has_args() ? ARG : 0;
    _used |= (PTR_INTERNAL_NEEDED|TYPE_INTERNAL_NEEDED);
  }
}

void ThisJoinPoint::check_field (const char* field, bool dyn_only) {
  if (strcmp (field, "signature") == 0)
    _used |= SIGNATURE;
  else if (strcmp (field, "filename") == 0)
    _used |= FILENAME;
  else if (strcmp (field, "line") == 0)
    _used |= LINE;
  else if (strcmp (field, "args") == 0)
    _used |= ARGS;
  else if (strcmp (field, "arg") == 0)
    _used |= ARG;
  else if (strcmp (field, "argtype") == 0)
    _used |= ARG_TYPE;
  else if (strcmp (field, "type") == 0)
    _used |= TYPE;
  else if (strcmp (field, "id") == 0 || (!dyn_only && strcmp (field, "JPID") == 0))
    _used |= ID;
  else if (strcmp (field, "resulttype") == 0)
    _used |= RESULT_TYPE;
  else if (strcmp (field, "that") == 0)
    _used |= THAT;
  else if (strcmp (field, "target") == 0)
    _used |= TARGET;
  else if (strcmp (field, "result") == 0)
    _used |= RESULT;
  else if (strcmp (field, "entity") == 0)
    _used |= ENTITY;
  else if (strcmp (field, "memberptr") == 0)
    _used |= MEMBERPTR;
  else if (strcmp (field, "array") == 0)
    _used |= ARRAY;
  else if (strcmp (field, "idx") == 0)
    _used |= IDX;
  else if (strcmp (field, "jptype") == 0)
    _used |= JP_TYPE;
  else if (strcmp (field, "action") == 0)
    _used |= ACTION;
  else if (strcmp (field, "wrapper") == 0)
    _used |= WRAPPER;
  else if (strcmp (field, "proceed") == 0) {
    _used |= PROCEED_ADVICE;
    _proceed_calls++;
  }
}

bool ThisJoinPoint::check_type (const string &name) {
  if (name == "JoinPoint" || starts_with(name, "JoinPoint::")) {
    _used |= TYPE_ADVICE_NEEDED;
    return true;
  }
  return false;
}

bool ThisJoinPoint::check_obj (const string &name) {
  if (name == "tjp") {
    _used |= (PTR_ADVICE_NEEDED | TYPE_ADVICE_NEEDED);
    return true;
  }
  else if (name == "thisJoinPoint") {
    _used |= (PTR_ADVICE_NEEDED | PTR_ALIAS_NEEDED | TYPE_ADVICE_NEEDED);
    return true;
  }
  return false;
}

// Checks if it is necessary to weave additionally things like call wrapper
// arguments to ensure correct runtime checks
void ThisJoinPoint::check_condition(const Condition& condition) {
  if (condition) {
    // that needed?
    TypeCheckSet tcs;
    condition.checks_for_that(tcs);
    if(!tcs.empty()) {
      _used |= THAT;
    }
  }
}


void ThisJoinPoint::merge_flags (ACM_CodePlan &plan) {
  if (plan.has_next_level())
    merge_flags(*plan.get_next_level());
  typedef ACM_Container<ACM_CodeAdvice, true> Container;
  Container &before = plan.get_before();
  for (Container::iterator i = before.begin(); i != before.end(); ++i)
    TI_CodeAdvice::of(*(*i))->get_advice_info()->addTJPFlags(*this);
  if (plan.has_around()) {
    TI_CodeAdvice::of(*plan.get_around())->get_advice_info()->addTJPFlags(*this);
    if (*TI_CodeAdvice::of(*plan.get_around())->get_condition())
      conditional();
  }
  Container &after = plan.get_after();
  for (Container::iterator i = after.begin(); i != after.end(); ++i)
    TI_CodeAdvice::of(*(*i))->get_advice_info()->addTJPFlags(*this);
}

// only required for features of the Clang variant
void ThisJoinPoint::merge_implicit( ACM_Code &loc ) {
  assert( loc.type_val() == JPT_Builtin ); // currently only builtin operator calls can have implicit joinpoints
  ACM_Builtin &node = static_cast<ACM_Builtin &>( loc );

  ThisJoinPoint collect;

  typedef ACM_Container<ACM_Access, true> Container;
  Container &implicit = node.get_implicit_access();
  for( Container::const_iterator it = implicit.begin(); it != implicit.end(); it++ ) {
    if( ( *it )->has_plan() )
      collect.merge_flags( *( ( *it )->get_plan() ) );
    if( TI_Code::of( **it )->has_implicit_joinpoints() )
      collect.merge_implicit( **it );
  }

  if( collect.useAction() ) { // if we use an action, make sure we have all we need
    collect._used |= THAT;
  }

  unsigned int mask = 0; // only forward a selection, as not all are shared
  mask |= THAT;

  // merge
  _used |= ( collect._used & mask );
}

bool ThisJoinPoint::arg_needed( ACM_Code *loc ) const {
  return arg() || useAction() || ( proceed() && proceed_needs_args( *loc ) );
}

bool ThisJoinPoint::that_needed( ACM_Code *loc ) const {
  return that() || useAction() || ( proceed() && proceed_needs_that( *loc ) );
}

bool ThisJoinPoint::target_needed( ACM_Code *loc ) const {
  return target() || useAction() || ( proceed() && proceed_needs_target( *loc ) );
}

bool ThisJoinPoint::result_needed( ACM_Code *loc ) const {
  return result() || useAction() || ( proceed() && proceed_needs_result( *loc ) );
}

bool ThisJoinPoint::entity_needed( ACM_Code *loc ) const {
  return _enable_entity && ( entity() || useAction() );
}

bool ThisJoinPoint::memberptr_needed( ACM_Code *loc ) const {
  return _enable_entity && memberptr();
}

bool ThisJoinPoint::memberptr_defined( ACM_Code *loc ) const {
  return has_entity( *loc ) && needs_this( *get_entity( *loc ) ) && ! ( loc->type_val() == JPT_Construction || loc->type_val() == JPT_Destruction );
}

bool ThisJoinPoint::array_needed( ACM_Code *loc ) const {
  return _enable_entity && ( array() || useAction() );
}

bool ThisJoinPoint::array_defined( ACM_Code *loc ) const {
  // arrays are only supported in the clang variant
  if( ! ( loc->type_val() & ( JPT_Access & ~ ( JPT_Call | JPT_Builtin ) ) ) )
    return false;

  TI_Access *ti = TI_Access::of( *static_cast<ACM_Access *>( loc ) );
  return ti->entity_index_count() > 0;
}

bool ThisJoinPoint::idx_needed( ACM_Code *loc ) const {
  return _enable_entity && ( idx() || useAction() );
}

void ThisJoinPoint::dump (ostream &out) const {
  out << "ThisJoinPoint (";
  out << "signature=" << (signature () ? "true" : "false");
  out << ", filename=" << (filename () ? "true" : "false");
  out << ", line=" << (line () ? "true" : "false");
  out << ", args=" << (args () ? "true" : "false");
  out << ", arg=" << (arg() ? "true" : "false");
  out << ", argtype=" << (argtype() ? "true" : "false");
  out << ", type=" << (type() ? "true" : "false");
  out << ", id=" << (id() ? "true" : "false");
  out << ", resulttype=" << (resulttype() ? "true" : "false");
  out << ", that=" << (that() ? "true" : "false");
  out << ", target=" << (target() ? "true" : "false");
  out << ", result=" << (result() ? "true" : "false");
  out << ", entity=" << (entity() ? "true" : "false");
  out << ", memberptr=" << (memberptr() ? "true" : "false");
  out << ", array=" << (array() ? "true" : "false");
  out << ", idx=" << (idx() ? "true" : "false");
  out << ", jptype=" << (jptype() ? "true" : "false");
  out << ", action=" << (action() ? "true" : "false");
  out << ", proceed=" << (proceed() ? "true" : "false");
  out << ")" << endl;
}
