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

#include <string.h>

#include "Puma/CClassDatabase.h"
#include "Puma/CArgumentInfo.h"

void AdviceInfo::gen_invocation_func (ostream &out, bool def) {

  CFunctionInfo *ad_func = _code->function ();
  CStructure *ad_cls = ad_func->ClassScope ();
  
  ThisJoinPoint &tjp = _code->this_join_point ();
  ThisJoinPoint &aspectof_tjp = aspect ()->aspectof_this_join_point ();
    
  // determine whether the invocation functions needs JoinPoint, tjp, or Bind.
  bool has_context = (ad_func->Arguments () > 0);
  bool type_needed = tjp.type_needed () || aspectof_tjp.type_needed () ||
                     has_context;
  bool pointer_needed = tjp.pointer_needed () || has_context ||
                        aspectof_tjp.pointer_needed ();
    
  if (type_needed) {
    out << "  template <class JoinPoint";
    if (has_context)
      out << ", class Binding";
    out << ">" << endl;
  }
  out << "  inline void invoke_" << _aspect->name () << "_"
      << Scope ()->Name () << "_" << (name () + 1) << " (";
  if (pointer_needed)
    out << "JoinPoint *tjp";
  out << ")" << (def ? " {" : ";") << endl;

  // the declaration ends here
  if (!def)
    return;
      
  // generate typedefs
  for (int a = 0; a < (int)ad_func->Arguments (); a++)
    out << "    typedef typename Binding::template Arg<" << a << "> Arg" << a 
        << ";" << endl;

  // generate advice call
  out << "    ";
  if (ad_cls != aspect ()->ClassInfo ())
    out << "((::" << ad_cls->QualName () << "*)";
  // aspectof function
  out << "::" << aspect ()->name () << "::aspectof";
  // generate <JoinPoint> if necessary
  if (aspectof_tjp.type_needed () && !aspectof_tjp.pointer_needed ())
    out << "<JoinPoint>";
  out << "(";
  if (aspectof_tjp.pointer_needed ())
    out << "tjp";
  out << ")";
    
  if (ad_cls != aspect ()->ClassInfo ())
    out << ")";
  out << "->";
  if (tjp.type_needed () && !tjp.pointer_needed())
    out << "template ";  
  out << "__" << (name () + 1);

  // generate <JoinPoint> if necessary
  if (tjp.type_needed () && !tjp.pointer_needed())
    out << "<JoinPoint>";
  out << " (";

  int nargs = 0;

  if (tjp.pointer_needed()) {
    out << "tjp";
    nargs++;
  }

  for (int a = 0; a < (int)ad_func->Arguments (); a++) {
    if (nargs > 0)
       out << ", ";
    out << "(" << *ad_func->Argument (a)->TypeInfo () << ")";
    out << "Arg" << a << "::val (tjp)";
    nargs++;
  }

  out << ");" << endl;

  out << "  }" << endl;
}


void AdviceInfo::gen_invocation_func_call (ostream &stmt, const char* tjp_tp,
                                           const char *tjp_obj) {
  ThisJoinPoint &tjp = _code->this_join_point ();
  ThisJoinPoint &aspectof_tjp = aspect ()->aspectof_this_join_point ();
    
  // determine whether the invocation functions needs JoinPoint or tjp
  bool has_context = (_code->function ()->Arguments () > 0);
  bool type_needed = tjp.type_needed () || aspectof_tjp.type_needed () ||
                     has_context;
  bool pointer_needed = tjp.pointer_needed () || has_context ||
                        aspectof_tjp.pointer_needed ();
    
  stmt << "AC::invoke_" << _aspect->name () << "_"
       << Scope ()->Name () << "_" << (name () + 1);
       
  if (type_needed) {
    stmt << "<" << tjp_tp;
    if (has_context) {
      string tp (tjp_tp);
      tp.erase (tp.length () - 2, tp.length ());
      stmt << ", Binding_" << tp << "_0_" << _aspect->name () << "_"
           << Scope ()->Name () << "_" << (name () + 1);
    }
    stmt << ">";
  }

  stmt << " (";
  if (pointer_needed)
    stmt << tjp_obj;
  stmt << ");";
}


void AdviceInfo::gen_binding_template (ostream &out, const char *jpname,
                                       const BackEndProblems &bep) {
  // later this will be passed as an argument
  Binding &bind = _code->binding ();

  // no code generation if no context variables are expected
  if (!bind._used)
    return;
      
  // generate the mapping of context variables to advice function arguments
  out << "struct Binding_" << jpname << "_" << _aspect->name () << "_"
      << Scope ()->Name () << "_" << (name () + 1) << " {" << endl;
  out << "  typedef " << jpname << " TJP;" << endl;
  out << "  template <int I";
  if (bep._spec_scope)
    out << ", int DUMMY = 0";
  out << "> struct Arg {" << endl;
  out << "    void val (TJP *tjp) {} // for VC7" << endl;
  out << "  };" << endl;

  CFunctionInfo *ad_func = _code->function ();
  for (int a = 0; a < (int)ad_func->Arguments (); a++) {
    CTypeInfo *argtype = ad_func->Argument (a)->TypeInfo ();
    int bind_index = bind.bound_to (ad_func->Argument (a));
    assert (bind_index != Binding::BIND_NOT_FOUND);
    out << "  template <";
    if (bep._spec_scope)
      out << "int DUMMY";
    out << "> struct Arg<" << a;
    if (bep._spec_scope)
      out << ", DUMMY";
    out << "> {" << endl
        << "    static TJP::";
    switch (bind_index) {
      case Binding::BIND_THAT:
        out << "That " << (argtype->isPointer () ? "*" : "&")
            << "val (TJP *tjp) { return " << (argtype->isPointer () ? "" : "*")
            << "tjp->that (); }" << endl;
        break;
      case Binding::BIND_TARGET:
        out << "Target " << (argtype->isPointer () ? "*" : "&")
            << "val (TJP *tjp) { return " << (argtype->isPointer () ? "" : "*")
            << "tjp->target (); }" << endl;
        break;
      case Binding::BIND_RESULT:
        out << "Result &val (TJP *tjp) { return *tjp->result (); }" << endl;
        break;
      case Binding::BIND_NOT_FOUND: // already handled by assertion
        break;
      default: // argument
        out << "Arg<" << bind_index << ">::ReferredType &val (TJP *tjp) {"
            << " return *tjp->template arg<" << bind_index << "> (); }" << endl;
    }
    out << "  };" << endl;
  }

  out << "};" << endl;  
}

