// 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                                            

// AspectC++ includes
#include "WeaverBase.h"
#include "ACUnit.h"
#include "LineDirectiveMgr.h"

// PUMA includes
#include "Puma/ErrorSink.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CArgumentInfo.h"
#include "Puma/CClassInfo.h"
#include "Puma/CTemplateInfo.h"
#include "Puma/CTemplateInstance.h"
#include "Puma/CTree.h"
#include "Puma/MacroUnit.h"

// C++ includes
#include <sstream>
using std::stringstream;
using std::endl;
using std::ends;
#include <cstring>
using std::strstr;

const WeavePos &WeaverBase::weave_pos (Token *t, WeavePos::Pos p) {

  // try to insert the position as a new one
  pair<WPSet::iterator, bool> result = _positions.insert (WeavePos (t, p));

  // the resulting iterator points either to the new or an existing entry
  return *result.first;
}

// insert the contents of a generated unit at a given position (with move)
void WeaverBase::paste (const WeavePos &pos, Unit *unit) {
  // return immediately if the there is a problem with macro manipulation
  if (macro_problem (pos))
    return;
    
  _commander.addBuffer (unit);
  Token *first = (Token*)unit->first ();
  Token *last  = (Token*)unit->last ();
  if (pos._pos == WeavePos::WP_BEFORE) {
    _commander.move_before (first, last, pos._token);
  }
  else {
    _commander.move (first, last, pos._token);
  }
}

// insert a generated string a given position
void WeaverBase::paste (const WeavePos &pos, const string &str) {
  // return immediately if the string to paste is empty
  if (str == "")
    return;
  
  ACUnit *unit = new ACUnit (_err);
  bool has_nl = (strstr (str.c_str (), "\n") != NULL);
  if (has_nl)
    _line_mgr.directive (*unit);
  *unit << str.c_str ();
  if (has_nl)
    _line_mgr.directive (*unit, (Unit*)pos._token->belonging_to (), pos._token);
  *unit << endu;
  paste (pos, unit);
}

// replace the text between two positions with some new text
void WeaverBase::replace (const WeavePos &from, const WeavePos &to,
                                             const string &str) {
  paste (from, str);
  kill (from, to);
}

// replace the text of a syntax tree with some new text
void WeaverBase::replace (CTree *node, const string &str) {
  const WeavePos &from = weave_pos (node->token (), WeavePos::WP_BEFORE);
  const WeavePos &to   = weave_pos (node->end_token (), WeavePos::WP_AFTER);
  replace (from, to, str);
}

// copy the text between two position to another location
void WeaverBase::copy (const WeavePos &from, const WeavePos &to, 
                       const WeavePos &dest) {
  assert (from._pos == WeavePos::WP_BEFORE);
  assert (to._pos == WeavePos::WP_AFTER);
  if (macro_problem (from) || macro_problem (to) || macro_problem (dest))
    return;
    
  if (dest._pos == WeavePos::WP_BEFORE) {
    {
      ACUnit *unit = new ACUnit (_err);
      _line_mgr.directive (*unit,
        (Unit*)from._token->belonging_to (), from._token);
      *unit << endu;
      if (unit->empty ()) delete unit; else paste (dest, unit);
    }
    _commander.copy_before (from._token, to._token, dest._token);
    {
      ACUnit *unit = new ACUnit (_err);
      _line_mgr.directive (*unit,
        (Unit*)dest._token->belonging_to (), dest._token);
      *unit << endu;
      if (unit->empty ()) delete unit; else paste (dest, unit);
    }
  }
  else {
    {
      ACUnit *unit = new ACUnit (_err);
      _line_mgr.directive (*unit,
        (Unit*)dest._token->belonging_to (), dest._token);
      *unit << endu;
      if (unit->empty ()) delete unit; else paste (dest, unit);
    }
    _commander.copy (from._token, to._token, dest._token);
    {
      ACUnit *unit = new ACUnit (_err);
      _line_mgr.directive (*unit,
        (Unit*)from._token->belonging_to (), from._token);
      *unit << endu;
      if (unit->empty ()) delete unit; else paste (dest, unit);
    }
  }
}
  
// copy the text of a syntax tree to another location
void WeaverBase::copy (CTree *node, const WeavePos &dest) {
  const WeavePos &from = weave_pos (node->token (), WeavePos::WP_BEFORE);
  const WeavePos &to   = weave_pos (node->end_token (), WeavePos::WP_AFTER);
  copy (from, to, dest);
}

// kill the text between two positions
void WeaverBase::kill (const WeavePos &from, const WeavePos &to) {
  // TODO: this should not matter, the code shoud be able to deal with it
  assert (from._pos == WeavePos::WP_BEFORE);
  assert (to._pos == WeavePos::WP_AFTER);
  if (macro_problem (from) || macro_problem (to))
    return;
    
  bool has_nl = (strstr (from._token->text ().c_str (), "\n") != NULL);
  if (to._token && from._token != to._token || has_nl)
    _line_mgr.insert ((Unit*)to._token->belonging_to (), to._token, true);
  _commander.kill (from._token, to._token);
}
  
// kill the text of a syntax tree
void WeaverBase::kill (CTree *node) {
  const WeavePos &from = weave_pos (node->token (), WeavePos::WP_BEFORE);
  const WeavePos &to   = weave_pos (node->end_token (), WeavePos::WP_AFTER);
  kill (from, to);
}

bool WeaverBase::commit ()
 {
   ManipError terror = _commander.valid ();
   if (!terror)
      _commander.commit ();
   else
      _err << terror << endMessage;
   return (bool)terror;
 }

bool WeaverBase::macro_problem (const WeavePos &pos) {
  Token *token = pos._token;
  Unit  *unit  = (Unit*)token->belonging_to ();
  assert (unit);
  if (unit->isMacroExp () &&
      ((pos._pos == WeavePos::WP_BEFORE &&
        unit->isMacroExp () && !((MacroUnit*)unit)->ExpansionBegin (token)) ||
       (pos._pos == WeavePos::WP_AFTER &&
           unit->isMacroExp () && !((MacroUnit*)unit)->ExpansionEnd (token)))) {
      if (_problems._warn_macro) {
        _err << sev_warning << token->location ()
             << "transformation within macro '"
             << ((MacroUnit*)unit)->MacroBegin ()->text () << "'" << endMessage;
      }
      return true;
  }
  return false;  
}

string WeaverBase::protection_string (CProtection::Type prot) {
  switch (prot) {
  case CProtection::PROT_PRIVATE:   return "\nprivate:\n";
  case CProtection::PROT_PROTECTED: return "\nprotected:\n";
  case CProtection::PROT_PUBLIC:    return "\npublic:\n";
  default:
    assert (false);
    return "";
  }
}

bool WeaverBase::needs_this (CFunctionInfo *func)
 {
  // check if the called function is a method (needs a 'this' pointer)
  if (func->isMethod () && !func->isStaticMethod ())
   {
     return !(func->isOperator () &&
	      (strcmp (func->Name (), "operator new") == 0 ||
	       strcmp (func->Name (), "operator new[]") == 0 ||
	       strcmp (func->Name (), "operator delete") == 0 ||
	       strcmp (func->Name (), "operator delete[]") == 0));
   }
  return false;
 }

void WeaverBase::print_tree (ostream &out, CTree *node)
 {
   if (node->NodeName () == CT_Token::NodeId ())
     out << node->token ()->text () << " ";
   else
      for (int s = 0; s < node->Sons (); s++)
	 print_tree (out, node->Son (s));
 }

CClassInfo *WeaverBase::cscope(CObjectInfo *obj) {
  CRecord *crec = obj->ClassScope ();
  return crec ? crec->ClassInfo () : (CClassInfo*)0;
}

CScopeInfo *WeaverBase::nscope(CObjectInfo *obj) {
  // template instances are stored in a special scope, better take the template
  if (obj->TemplateInstance ())
    obj = obj->TemplateInstance ()->Template ();
    
  // functions that are only friend declarations of an assigned scope
  CScopeInfo *assigned = obj->AssignedScope ();
  if (assigned)
    return (assigned->GlobalScope () || assigned->isAnonymous ()) ? 0:assigned;

  CRecord *crec = obj->ClassScope ();
  CScopeInfo *qual = obj->QualifiedScope ();
  CScopeInfo *scope = obj->Scope ();
  return crec ? (CScopeInfo*)crec : (qual ? qual :
    ((scope->GlobalScope () || scope->isAnonymous ()) ? 0 : scope));
}

void WeaverBase::rename_args (CFunctionInfo *func, const char * new_name) {
  // first rename the arguments in the argument list  
  for (unsigned arg = 0; arg < func->Arguments (); arg++)
    rename (func->Argument (arg), new_name, arg);

  // now rename the argument references in the constructor initialization list
  if (func->isConstructor () && func->Tree ()) {
    CTree *init = ((CT_FctDef*)func->Tree ())->CtorInit ();
    if (init) {
      for (unsigned arg = 0; arg < func->Arguments (); arg++)
        rename_simple_name (func->Argument (arg), new_name, arg, init);
    }
  }
}

void WeaverBase::rename (CArgumentInfo *arg, const string &new_name, int app) {
  // don't change anything with void arguments
  if (arg->TypeInfo ()->is_void ())
    return;
    
  // find the syntax tree node of the argument name
  CT_ArgDecl *arg_decl = (CT_ArgDecl*)arg->Tree ();
  CT_Declarator *declarator = (CT_Declarator*)arg_decl->Declarator ();
  CT_SimpleName *arg_name = name (declarator);

  // create the new name
  ostringstream name;
  name << " " << new_name; 
  if (app >= 0) // append an index to the name?
    name << app;
  name << ends;

  if (arg_name->NodeName () != CT_PrivateName::NodeId ()) {
    replace (arg_name, name.str ());
  }
  else {
    // in the case of an anonymous argument I have to find an insert pos
    CTree *left = 0, *right = 0;
    if (declarator) {
      if (declarator->NodeName () == CT_InitDeclarator::NodeId ())
        right = ((CT_InitDeclarator*)declarator)->Initializer ();
      else if (declarator->NodeName () == CT_BracedDeclarator::NodeId ())
        right = declarator->Son (declarator->Sons () - 1);
      else if (declarator->NodeName () == CT_ArrayDeclarator::NodeId ())
        right = declarator->Son (1);
      else if (declarator->NodeName () == CT_FctDeclarator::NodeId ())
        right = declarator->Son (1);
      else if (declarator->NodeName () == CT_RefDeclarator::NodeId ())
        left = declarator->Son (0);
      else if (declarator->NodeName () == CT_PtrDeclarator::NodeId ())
        left = declarator->Son (declarator->Sons () - 2);
      else if (declarator->NodeName () == CT_MembPtrDeclarator::NodeId ())
        left = declarator->Son (declarator->Sons () - 2);
      else if (declarator->NodeName () == CT_BitFieldDeclarator::NodeId ())
        right = declarator->Son (1);
      else {
        _err << sev_fatal << declarator->token ()->location ()
             << "unknown declarator type" << endMessage;
        return;
      }
    }
    if (!left && !right) {
      // no declarator + anonymous => there must be at least a decl spec seq!
      left = arg_decl->DeclSpecs ();
    }
    if (left) {
      const WeavePos &lp = weave_pos (left->end_token (), WeavePos::WP_AFTER);
      paste (lp, name.str ());
    }
    else if (right) {
      const WeavePos &rp = weave_pos (right->token (), WeavePos::WP_BEFORE);
      paste (rp, name.str ());
    }
    else
      _err << sev_fatal << declarator->token ()->location ()
           << "found no hook to replace ananymous arg name" << endMessage;
  }
}

void WeaverBase::rename_simple_name (CObjectInfo *renamed_obj,
                                      const char *new_name, int app,
                                      CTree *node) {
  const char *nodename = node->NodeName ();
  if (nodename == CT_SimpleName::NodeId ()) {
    CObjectInfo *obj = ((CT_SimpleName*)node)->Object ();
    if (obj == renamed_obj) {
      // create a unit with the new name
      ACUnit *name_unit = new ACUnit (_err); 
      _commander.addBuffer (name_unit);
      *name_unit << new_name;
      if (app >= 0) // append an index to the name?
        *name_unit << app;
      *name_unit << endu;

      // replace the token with the generated name
      _commander.replace (node->token (), node->end_token (),
        (Token*)name_unit->first (), (Token*)name_unit->last ());
    }
  }

  for (int s = 0; s < node->Sons (); s++)
    rename_simple_name (renamed_obj, new_name, app, node->Son (s));
}   

CT_SimpleName *WeaverBase::is_name (CTree *node) {
  if (node->NodeName () == CT_QualName::NodeId () ||
      node->NodeName () == CT_SimpleName::NodeId () ||
      node->NodeName () == CT_OperatorName::NodeId () ||
      node->NodeName () == CT_ConversionName::NodeId () ||
      node->NodeName () == CT_RootQualName::NodeId () ||
      node->NodeName () == CT_PrivateName::NodeId ())
    return (CT_SimpleName*)node;
  else
    return (CT_SimpleName*)0;
}

CT_FctDeclarator *WeaverBase::fct_declarator (CT_Declarator *declarator) {
  do {
    CT_Declarator *next = (CT_Declarator*)declarator->Declarator ();
    if (declarator->NodeName () == CT_FctDeclarator::NodeId () &&
	is_name (next))
      return (CT_FctDeclarator*)declarator;
    declarator = next;
  } while (!is_name (declarator));
  return (CT_FctDeclarator*)0;
}

CT_SimpleName *WeaverBase::name (CT_Declarator *&declarator) {
  CTree *node = declarator;
  declarator  = (CT_Declarator*)0;
  while (!is_name (node)) {
    declarator = (CT_Declarator*)node;
    node = declarator->Declarator ();
  }
      
  return (CT_SimpleName*)node;
}

// return LinkageSpec node for extern "C" <decl> like declarations
CTree *WeaverBase::linkage_adjust (CT_Decl *decl) {
  CT_LinkageSpec *linkage = decl->Linkage ();
  return (!linkage || linkage->isList ()) ? decl : linkage;
}
