// 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 "Plan.h"
#include "AspectInfo.h"
#include "AdviceInfo.h"
#include "IntroductionInfo.h"
#include "OrderInfo.h"

#include "Puma/ACAspectInfo.h"
#include "Puma/ACTree.h"
#include "Puma/ErrorSink.h"

Plan::~Plan () {
  for (int i = 0; i < (int)_advice_codes.length (); i++)
    delete _advice_codes[i];
  for (int i = 0; i < (int)_advice_infos.length (); i++)
    delete _advice_infos[i];
  for (int i = 0; i < (int)_introduction_infos.length (); i++)
    delete _introduction_infos[i];
  for (int i = 0; i < (int)_aspect_infos.length (); i++)
    delete _aspect_infos[i];
  for (int i = 0; i < (int)_exec_jpls.length (); i++)
    delete _exec_jpls[i]->plan ();
  for (int i = 0; i < (int)_call_jpls.length (); i++)
    delete _call_jpls[i]->plan ();
  for (int i = 0; i < (int)_cons_jpls.length (); i++)
    delete _cons_jpls[i]->plan ();
  for (int i = 0; i < (int)_dest_jpls.length (); i++)
    delete _dest_jpls[i]->plan ();
}

AspectInfo *Plan::addAspect (ACAspectInfo *ai) {
  AspectInfo *result = new AspectInfo (ai);
  _aspect_infos.append (result);
  return result;
}

AspectInfo *Plan::getAspect(ACAspectInfo *ai) const {
  AspectInfo *result = 0;
  for (int i = 0; i < _aspect_infos.length (); i++) {
    if (_aspect_infos.lookup (i)->acnode () == ai) {
      result = _aspect_infos.lookup (i);
      break;
    }
  }
  return result;
}

AdviceCode *Plan::addAdviceCode (CT_AdviceDecl *ad) {
  AdviceCode *result = new AdviceCode (ad);
  _advice_codes.append (result);
  return result;
}

AdviceInfo *Plan::addAdvice (AspectInfo *ai, CT_AdviceDecl *ad) {
  AdviceCode *code = 0;
  for (int i = 0; i < _advice_codes.length (); i++) {
    AdviceCode *curr = _advice_codes[i];
    if (curr->tree () == ad) {
      code = curr;
      break;
    }
  }
  assert (code);
  AdviceInfo *result = new AdviceInfo (ai, code);
  _advice_infos.append (result);
  ai->advice_infos ().push_back (result);
  return result;
}

IntroductionInfo *Plan::addIntroduction (AspectInfo *ai,
           ACIntroductionInfo *acii) {
  IntroductionInfo *result = new IntroductionInfo (ai, acii);
  _introduction_infos.append (result);
  ai->intro_infos ().push_back (result);
  return result;
}

OrderInfo *Plan::addOrder (AspectInfo *ai, CT_AdviceDecl *ad) {
  OrderInfo *result = new OrderInfo (ai, ad);
  _order_infos.append (result);
  ai->order_infos ().push_back (result);
  return result;
}

// consider a join point and advice in the plan
void Plan::consider (JoinPointLoc *jpl, const Condition &cond, AdviceInfo *ai) {
  JPL_Code *jp_code = (JPL_Code*)jpl;
  if (!jp_code->is_pseudo ()) {
    JPP_Code *jp_plan = (JPP_Code*)jpl->plan ();
    if (!jp_plan) {
      switch (jpl->type ()) {
      case JoinPointLoc::Method:
        jp_plan = new JPP_Code;
        _exec_jpls.append (jpl);
        break;
      case JoinPointLoc::MethodCall:
        jp_plan = new JPP_Code;
        _call_jpls.append (jpl);
        break;
      case JoinPointLoc::Construction:
        jp_plan = new JPP_Code;
        _cons_jpls.append (jpl);
        break;
      case JoinPointLoc::Destruction:
        jp_plan = new JPP_Code;
        _dest_jpls.append (jpl);
        break;
      default:
        _err << sev_error
             << "internal problem, invalid join point type in plan" 
             << endMessage;
        return;
      }
      jpl->plan (jp_plan);
    }
    jp_plan->consider (ai, cond);
  }

  if (cond) {
    CFunctionInfo *that_func = 0, *target_func = 0;
    
    switch (jpl->type ()) {
    case JoinPointLoc::Method:
      that_func = ((JPL_Method*)jpl)->func_info ();
      break;
    case JoinPointLoc::Construction:
      that_func = ((JPL_Construction*)jpl)->func_info ();
      break;
    case JoinPointLoc::Destruction:
      that_func = ((JPL_Destruction*)jpl)->func_info ();
      break;
    case JoinPointLoc::MethodCall:
      that_func = ((JPL_MethodCall*)jpl)->caller ();
      target_func = ((JPL_MethodCall*)jpl)->called ();
      break;
    default:
      assert (false);
    }

    cond.checks_for_that (_type_checks_true);
    cond.checks_for_target (_type_checks_true);

    StringSet check_names_that, check_names_target;

    if (that_func) {
      cond.names_for_that (check_names_that);
      for (StringSet::iterator iter = check_names_that.begin ();
           iter != check_names_that.end (); ++iter) {
        _type_checks_false.insert (TypeCheck (that_func->ClassScope (),
                                   *iter));
      }
    }

    if (target_func) {
      cond.names_for_target (check_names_target);
      for (StringSet::iterator iter = check_names_target.begin ();
	   iter != check_names_target.end (); ++iter) {
	_type_checks_false.insert (TypeCheck (target_func->ClassScope (), 
					      *iter));
      }
    }
  }
}

void Plan::consider (JoinPointLoc *jpl, const CFlow &cflow) {
  JPL_Code *jp_code = (JPL_Code*)jpl;
  if (jp_code->is_pseudo ())
    return;
    
  JPP_Code *jp_plan = (JPP_Code*)jpl->plan ();
  if (!jp_plan) {
    switch (jpl->type ()) {
    case JoinPointLoc::Method:
      jp_plan = new JPP_Code;
      _exec_jpls.append (jpl);
      break;
    case JoinPointLoc::MethodCall:
      jp_plan = new JPP_Code;
      _call_jpls.append (jpl);
      break;
    case JoinPointLoc::Construction:
      jp_plan = new JPP_Code;
      _cons_jpls.append (jpl);
      break;
    case JoinPointLoc::Destruction:
      jp_plan = new JPP_Code;
      _dest_jpls.append (jpl);
      break;
    default:
      _err << sev_error
           << "internal problem, invalid join point type in plan" 
           << endMessage;
      return;
    }
    jpl->plan (jp_plan);
  }
  jp_plan->consider (cflow);
}

// consider a join point for an introduction in the plan
void Plan::consider (JoinPointLoc *jpl, IntroductionInfo *ii) {
  JPP_Class *jp_plan = (JPP_Class*)jpl->plan ();
  if (!jp_plan) {
    switch (jpl->type ()) {
    case JoinPointLoc::Class:
    case JoinPointLoc::Aspect:
      jp_plan = new JPP_Class;
      _class_jpls.append (jpl);
      break;
    default:
      _err << sev_error
	   << "internal problem, invalid join point type for intro in plan" 
	   << endMessage;
      return;
    }
    jpl->plan (jp_plan);
  }
  
  jp_plan->consider (ii);
}
  
// consider ordering information in the plan
void Plan::consider(JoinPointLoc* jpl, ACAspectInfo &h, ACAspectInfo &l) {
  JoinPointPlan* jp_plan = jpl->plan();
  if (jp_plan) {
    jp_plan->consider(h, l);
  }
}

// check the final plan -> messages to the error sink
void Plan::check () {
  for (int i = 0; i < (int)_exec_jpls.length (); i++)
    if (!_exec_jpls[i]->plan ()->check (_err)) {
      _err << " for execution(\"" << _exec_jpls[i]->signature () << "\")"
	   << _exec_jpls[i]->tree ()->token ()->location () << endMessage;
      break;
    }
  for (int i = 0; i < (int)_call_jpls.length (); i++)
    if (!_call_jpls[i]->plan ()->check (_err)) {
      _err << " for call(\"" << _call_jpls[i]->signature () << "\")"
	   << _call_jpls[i]->tree ()->token ()->location () << endMessage;
      break;
    }
  for (int i = 0; i < (int)_cons_jpls.length (); i++)
    if (!_cons_jpls[i]->plan ()->check (_err)) {
      _err << " for construction(\"" << _cons_jpls[i]->signature () << "\")"
     << _cons_jpls[i]->tree ()->token ()->location () << endMessage;
      break;
    }
  for (int i = 0; i < (int)_dest_jpls.length (); i++)
    if (!_dest_jpls[i]->plan ()->check (_err)) {
      _err << " for destruction(\"" << _dest_jpls[i]->signature () << "\")"
     << _dest_jpls[i]->tree ()->token ()->location () << endMessage;
      break;
    }
  for (int i = 0; i < (int)_class_jpls.length (); i++) 
    if (!_class_jpls[i]->plan ()->check (_err)) {
      _err << " for class \"" << _exec_jpls[i]->signature () << "\""
	   << _class_jpls[i]->tree ()->token ()->location () << endMessage;
      break;
    }
}
