// 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 <assert.h>

#include "ThisJoinPoint.h"
#include "JoinPoint.h"
#include "Naming.h"
#include "Binding.h"
#include "JPAdvice.h"
#include "JoinPointLoc.h"

#include "Puma/CTree.h"
#include "Puma/CClassInfo.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CTypeInfo.h"
#include "Puma/CArgumentInfo.h"

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

static CClassInfo *cscope(CObjectInfo *obj) {
  if (obj->QualifiedScope ())
    return obj->QualifiedScope ()->ClassInfo ();
  return obj->Scope ()->ClassInfo ();
}

// special setup function for bindings
void ThisJoinPoint::setup (const Binding &binding) {
  _setup = true;
  if (binding._used) {
    _used |= (binding._this != 0) ? THAT : 0;
    _used |= (binding._target != 0) ? TARGET : 0;
    _used |= (binding._result != 0) ? RESULT : 0;
    _used |= (binding._args.length () > 0) ? ARG : 0;
    _used |= (PTR_NEEDED|TYPE_NEEDED);
  }
}

void ThisJoinPoint::setup (CFunctionInfo* func) {
  assert (func->Tree ()->NodeName () == CT_FctDef::NodeId ());

  _setup = true;
  // by starting with the second statement of the body we avoid to iterate
  // through the generated argument list and the generated typedef at the
  // beginning of the function
  CT_CmpdStmt *body = ((CT_FctDef*)func->Tree ())->Body ();
//  for (int i = 1; i < body->Entries (); i++)
// EXPERIMENT
  for (int i = 0; i < body->Entries (); i++)
    setup_search (func, body->Entry (i));
}

void ThisJoinPoint::setup_search (CFunctionInfo *func, CTree *node) {
  CObjectInfo *obj;
  CClassInfo *cls;

  const char *nodename = node->NodeName ();

  if (nodename == CT_SimpleName::NodeId () &&
      (obj = ((CT_SimpleName*)node)->Object ())) {
    if (obj->Scope ()->GlobalScope () &&
        strcmp (obj->Name (), "tjp") == 0)
      _used |= (PTR_NEEDED | TYPE_NEEDED);
    else if (obj->Scope ()->GlobalScope () &&
             strcmp (obj->Name (), "thisJoinPoint") == 0)
      _used |= (PTR_NEEDED | PTR_ALIAS_NEEDED | TYPE_NEEDED);
    else if (obj->Scope ()->GlobalScope () &&
             strcmp (obj->Name (), "JoinPoint") == 0)
      _used |= TYPE_NEEDED;
  
    // workaround as long as calls to template functions are not resolved
    if (obj->ClassScope () && 
        strcmp (obj->ClassScope ()->Name (), "JoinPoint") == 0 &&
        obj->FunctionInfo () && obj->FunctionInfo ()->Arguments () == 0 &&
        strcmp (obj->Name (), "arg") == 0) {
      _used |= ARG;
    }
  }

  if (nodename == CT_CallExpr::NodeId () &&
      (obj = ((CT_CallExpr*)node)->Object ()) &&
      (cls = obj->DefObject ()->Scope ()->ClassInfo ()) &&
      strcmp (cls->QualName (), "JoinPoint") == 0) {
    const char *field = obj->Name ();

    if (strcmp (field, "signature") == 0)
      _used |= SIGNATURE;
    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)
      _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, "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;
  }

  for (int s = 0; s < node->Sons (); s++)
    setup_search (func, node->Son (s));
}   

void ThisJoinPoint::make_result_type(ostream &out, CTypeInfo *type,
				     const char *rt_name) {
  if (type->isVoid () || type->isUndefined ())
    out << "void " << rt_name;
  else if (type->isAddress ()) {
    string ptr_name = "*";
    ptr_name += rt_name;
    type->BaseType ()->TypeText (out, ptr_name.c_str (), true, true);
  }
  else
    type->TypeText (out, rt_name, true, true);
}

void ThisJoinPoint::gen_tjp_struct (ostream &code, JPL_Code *loc,
                                    BackEndProblems &bep, int depth) const {

  // the level 0 struct -> contains all the data and types
  code << "struct ";
  Naming::tjp_struct (code, loc, depth);
  if (depth > 0) {
    // the level N > 0 structs -> are derived from level N - 1
    code << " : ";
    Naming::tjp_struct (code, loc, depth - 1);
  }
  else {
    // the level N == 0 structs are derived from AC::Action, if action() used
    if (useAction())
      code << " : AC::Action";
  }
  code << " {" << endl;
  
  stringstream types, data, fct;

  // start: type definitions --------------------------------------------------
  // Result type
  types << "  typedef ";
  make_result_type (types, loc->result_type ().type_info (), "Result");
  types << ";" << endl;

  // that type
  types << "  typedef ";
  ((JPL_Code*)loc)->that_type ()->TypeText (types, "That", true, true);
  types << ";" << endl;

  // target type
  types << "  typedef ";
  ((JPL_Code*)loc)->target_type ()->TypeText (types, "Target", true, true);
  types << ";" << endl;
    
  // join point id and type;
  types << "  static const int JPID = " << loc->id () << ";" << endl;
  types << "  static const AC::JPType JPTYPE = (AC::JPType)" << loc->type ()
        << ";" << endl;
        
  // result type
  types << "  struct Res {" << endl;
  CTypeInfo *restype = loc->result_type ().type_info ();
  if (restype->isUndefined ()) {
    types << "    typedef void Type;" << endl;
    types << "    typedef void ReferredType;" << endl;
  }
  else {
    types << "    typedef ";
    restype->TypeText (types, "Type", true, true);
    types << ";" << endl;
    CTypeInfo *refrestype = restype;
    if (refrestype->isAddress ())
      refrestype = restype->BaseType ();
    types << "    typedef ";
    refrestype->TypeText (types, "ReferredType", true, true);
    types << ";" << endl;
  }
  types << "  };" << endl;
  
  // argument types
  unsigned arg_count = loc->arg_count ();
  types << "  enum { ARGS = " << arg_count << " };" << endl;
  types << "  template <int I";
  if (bep._spec_scope)
    types << ", int DUMMY = 0";
  types << "> struct Arg {" << endl;
  types << "    typedef void Type;" << endl;
  types << "    typedef void ReferredType;" << endl;
  types << "  };" << endl;
  for (unsigned a = 0; a < arg_count; a++) {
    CTypeInfo *argtype = loc->arg_type (a).type_info ();
    types << "  template <";
    if (bep._spec_scope)
      types << "int DUMMY";
    types << "> struct Arg<" << a;
    if (bep._spec_scope)
      types << ", DUMMY";
    types << "> {" << endl;
    types << "    typedef ";
    argtype->TypeText (types, "Type", true, true);
    types << ";" << endl;
    CTypeInfo *refargtype = argtype;
    if (refargtype->isAddress ())
      refargtype = argtype->BaseType ();
    types << "    typedef ";
    refargtype->TypeText (types, "ReferredType", true, true);
    types << ";" << endl;
    types << "  };" << endl;
  }
  
  // begin of static data -----------------------------------------------------
  if (signature ()) {
    fct << "  inline static const char *signature () { return \"";
    fct << loc->signature () << "\";}" << endl;
  }
  if (args ()) {
    fct << "  inline static const int args () { return ";
    fct << arg_count << ";";
    fct << " }" << endl;
  }
  if (type()) {
    fct << "  inline static  AC::Type type() { return ";
    if (loc->tjp_type()) {
      fct << "\"";
      loc->tjp_type()->TypeInfo()->Mangled(fct);
      fct << "\";";
    } else fct << "\"<unknown type>\";";
    fct << "}" << endl;
  }
  if (id()) {
    fct << "  inline static unsigned int id() { return JPID; }" << endl;
  }
  
  if (resulttype()) {
    fct << "  inline static AC::Type resulttype() {return ";
    if (!loc->result_type ().type_info ()->isUndefined ()) {
      fct << "\"";
      loc->result_type().type_info()->Mangled(fct);
      fct << "\";";
    } else fct << "\"<unknown signature>\";";
    fct << "}" << endl;    
  }
  
  if (jptype()) {
    fct << "  inline static AC::JPType jptype() { return JPTYPE; }" << endl;
  }
  
  if (argtype()) {
    fct << "  inline static AC::Type const argtype(unsigned i) {" << endl;
    if (arg_count > 0) {
      fct << "    static AC::Type const type[" << arg_count << "] = {";
      for (unsigned i = 0; i < arg_count; i++) {
        if (i > 0) fct << ", ";
        fct << "\"";
        loc->arg_type (i).type_info ()->Mangled(fct);
        fct << "\"";
      }
      fct << "};" << endl;
      fct << "    return type[i];" << endl;
      fct << "  }" << endl;
    } else {
      fct << "    return \"\";" << endl;
      fct << "  }" << endl;
    }
  }
   
  // begin of dynamic data ----------------------------------------------------
  if (arg () || useAction () || (proceed () && loc->proceed_needs_args ())) {
    if (!useAction ())
      data << "  void **_args;" << endl;
    fct <<  "  inline void *arg (int n) {return _args[n];}" << endl;
    fct <<  "  template <int I> typename Arg<I>::ReferredType *arg () {"
        << endl;
    fct <<  "    return (typename Arg<I>::ReferredType*)arg (I);" << endl;
    fct <<  "  }" << endl;
  }

  if (result() || useAction () || (proceed () && loc->proceed_needs_result ())) {
    if (!useAction ())
      data << "  Result *_result;" << endl;
    fct <<  "  inline Result *result() {return (Result*)_result;}" << endl;
  }

  if (target() || useAction () || (proceed () && loc->proceed_needs_target ())) {
    if (!useAction ())
      data << "  Target *_target;" << endl;
    fct <<  "  inline Target *target() {return (Target*)_target;}" << endl;
  }

  if (that() || useAction () || (proceed () && loc->proceed_needs_that ())) {
    if (!useAction ())
      data << "  That *_that;" << endl;
    fct <<  "  inline That *that() {return (That*)_that;}" << endl;
  }

  if (proceed () && loc->proceed_needs_fptr ()) {
    data << "  void *_fptr;" << endl;
  }
  
  if (wrapper() || useAction ()) {
    if (!useAction ())
      data << "  void (*_wrapper)(AC::Action &);" << endl;
    fct <<  "  inline void (*wrapper ())(AC::Action &) {return _wrapper;}" << endl;
  }

  // terminate all the strings
  types << endl << ends;
  data << endl << ends;
  fct << endl << ends;
  
  // add all types, attributes, and functions (on level 0)
  if (depth == 0) {
    code << types.str ().data ();
    code << data.str ().data ();
    code << fct.str ().data ();
  }
  
  // the closing bracket is *NOT* generated here -> for external extensions
}

void ThisJoinPoint::dump (ostream &out)
 {
   out << "ThisJoinPoint (";
   out << "signature=" << (signature () ? "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 << ", jptype=" << (jptype() ? "true" : "false");
   out << ", action=" << (action() ? "true" : "false");
   out << ", proceed=" << (proceed() ? "true" : "false");
   out << ")" << endl;
 }
