// 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 <string.h>
#include <ctype.h>

#include "Naming.h"
#include "JoinPointLoc.h"
#include "AdviceInfo.h"
#include "JoinPoint.h"
#include "AspectInfo.h"

#include "Puma/CFunctionInfo.h"
#include "Puma/CArgumentInfo.h"
#include "Puma/CClassInfo.h"
#include "Puma/ACAspectInfo.h"
#include "Puma/CTypeInfo.h"
#include "Puma/CLinkage.h"
#include "Puma/CScopeInfo.h"
#include "Puma/FileUnit.h"

void Naming::scope_name (ostream &out, CScopeInfo *scope) {
  assert (scope);
  // check if we reached the global scope
  if (scope->FileInfo ())
    return; 

  // print the parent scope first
  if (scope->Scope ())
    scope_name (out, scope->Scope ());  
    
  // print the scope name, preceeded by the length of the string
  if (scope->isAnonymous ())
    out << "4ANON"; // handle this better!
  else
    out << strlen (scope->Name ()) << scope->Name ();
}

bool Naming::unary (CFunctionInfo *func) {
  return ((func->TypeInfo()->isMethod() && func->Arguments() == 0) ||
    (!func->TypeInfo()->isMethod() && func->Arguments() == 1));
}

void Naming::conv_name (ostream &out, CFunctionInfo *func)
 {
   out << "cv";
   func->ConversionType ()->Mangled (out);
 }
   
void Naming::op_name (ostream &out, CFunctionInfo *func)
 {
   const char *name = func->Name () + strlen ("operator ");

   if (strcmp (name, "new") == 0)
      out << "nw";
   else if (strcmp (name, "new[]") == 0)
      out << "na";
   else if (strcmp (name, "delete") == 0)
      out << "dl";
   else if (strcmp (name, "delete[]") == 0)
      out << "da";
   else if (strcmp (name, "-") == 0 && unary (func))
      out << "ng";
   else if (strcmp (name, "&") == 0 && unary (func))
      out << "ad";
   else if (strcmp (name, "*") == 0 && unary (func))
      out << "de";
   else if (strcmp (name, "~") == 0)
      out << "co";
   else if (strcmp (name, "+") == 0)
      out << "pl";
   else if (strcmp (name, "-") == 0 && !unary (func))
      out << "mi";
   else if (strcmp (name, "*") == 0 && !unary (func))
      out << "ml";
   else if (strcmp (name, "/") == 0)
      out << "dv";
   else if (strcmp (name, "%") == 0)
      out << "rm";
   else if (strcmp (name, "&") == 0 && !unary (func))
      out << "an";
   else if (strcmp (name, "|") == 0)
      out << "or";
   else if (strcmp (name, "^") == 0)
      out << "eo";
   else if (strcmp (name, "=") == 0)
      out << "aS";
   else if (strcmp (name, "+=") == 0)
      out << "pL";
   else if (strcmp (name, "-=") == 0)
      out << "mI";
   else if (strcmp (name, "*=") == 0)
      out << "mL";
   else if (strcmp (name, "/=") == 0)
      out << "dV";
   else if (strcmp (name, "%=") == 0)
      out << "rM";
   else if (strcmp (name, "&=") == 0)
      out << "aN";
   else if (strcmp (name, "|=") == 0)
      out << "oR";
   else if (strcmp (name, "^=") == 0)
      out << "eO";
   else if (strcmp (name, "<<") == 0)
      out << "ls";
   else if (strcmp (name, ">>") == 0)
      out << "rs";
   else if (strcmp (name, "<<=") == 0)
      out << "lS";
   else if (strcmp (name, ">>=") == 0)
      out << "rS";
   else if (strcmp (name, "==") == 0)
      out << "eq";
   else if (strcmp (name, "!=") == 0)
      out << "ne";
   else if (strcmp (name, "<") == 0)
      out << "lt";
   else if (strcmp (name, ">") == 0)
      out << "gt";
   else if (strcmp (name, "<=") == 0)
      out << "le";
   else if (strcmp (name, ">=") == 0)
      out << "ge";
   else if (strcmp (name, "!") == 0)
      out << "nt";
   else if (strcmp (name, "&&") == 0)
      out << "aa";
   else if (strcmp (name, "||") == 0)
      out << "oo";
   else if (strcmp (name, "++") == 0)
      out << "pp";
   else if (strcmp (name, "--") == 0)
      out << "mm";
   else if (strcmp (name, ",") == 0)
      out << "cm";
   else if (strcmp (name, "->*") == 0)
      out << "pm";
   else if (strcmp (name, "->") == 0)
      out << "pt";
   else if (strcmp (name, "()") == 0)
      out << "cl";
   else if (strcmp (name, "[]") == 0)
      out << "ix";
   else if (strcmp (name, "?") == 0)
      out << "qu";
   else
    {
      cout << "no mangling for unknown operator \"" << name << "\""
	   << endl;
      out << "unknown_operator";
    }
 }

void Naming::constr_name (ostream &out, CFunctionInfo *func) {
  out << "C1";
}

void Naming::destr_name (ostream &out, CFunctionInfo *func) {
  out << "D1";
}

void Naming::mangle (ostream &out, CObjectInfo *obj) {
  
  // check if this object is a function:
  CFunctionInfo *func = obj->FunctionInfo ();

  // determine the scope
  CScopeInfo *scope = obj->ClassScope (); // class scope
  if (!scope)
    scope = obj->Scope (); // lexical scope
  
  // objects with C linkage and global namespace variables are not mangled
  if (obj->Language () == CLanguage::LANG_C ||
      (scope->GlobalScope () && !obj->FunctionInfo ())) {
    out << obj->Name ();
    return;
  }
  
  // mangle the function name
  out << "_Z";
   
  // print the (possibly nested) scope of the function
  if (scope) {
    out << "N";
    // possible CV qualifier of member functions must be printed here
    if (func) {
      if (func->TypeInfo ()->isConst ())
        out << "K";
      if (func->TypeInfo ()->isVolatile ())
        out << "V";
    }
    scope_name (out, scope);
  }

  if (func) {  
    // print the function name (special handling)   
    if (func->isOperator ())
      op_name (out, func);
    else if (func->isConversion ())
      conv_name (out, func);
    else if (func->isConstructor ())
      constr_name (out, func);   
    else if (func->isDestructor ())
      destr_name (out, func);   
    else
      out << strlen (func->Name ()) << func->Name ();
  }
  else {
    out << strlen (obj->Name ()) << obj->Name ();
  }
  
  // if this was a nested name, add the 'E'
  if (scope)
    out << "E";

  if (func) {
    // print the argument types (also mangled)
    if (func->Arguments () == 0)
      out << "v";
    else {
      for (unsigned a = 0; a < func->Arguments (); a++)
        func->Argument (a)->TypeInfo ()->Mangled (out);
    }
  }
}

void Naming::call_wrapper (ostream& out, JPL_MethodCall *jpl, unsigned depth) {
  out << "__call_";
  mangle (out, jpl->assoc_obj ());
  if (jpl->lid () >= 0)
    out << "_" << jpl->lid ();
  out << "_" << depth;
}

void Naming::exec_inner (ostream& out, JoinPointLoc *jpl) {
  CFunctionInfo *func = jpl->assoc_obj ()->FunctionInfo ();
  assert (func);
  out << "__exec_old_";
  if (func->isConstructor ())
    constr_name (out, func);
  else if (func->isDestructor ())
    destr_name (out, func);
  else if (func->isOperator ())
    op_name (out, func);
  else if (func->isConversion ())
    conv_name (out, func);
  else
    out << func->Name ();
}

void Naming::action_wrapper (ostream &out, JoinPointLoc *loc, unsigned depth) {
//  out << "__action_";
//  switch (loc->type ()) {
//  case JoinPointLoc::Method: out << "exec_"; break;
//  case JoinPointLoc::MethodCall: out << "call_"; break;
//  default: out << "unknown_";
//  }
//
//  mangle (out, loc->assoc_obj ());
//  if (loc->lid() >= 0) out << "_" << loc->lid ();
//  out << "_" << depth;
  out << "__action_func";
}

void Naming::exec_advice (ostream& out, JPL_Method *jpl, AdviceInfo *advice) {
  out << "__" << (advice->name () + 1);
}

void Naming::call_advice (ostream& out, JPL_MethodCall *jpl, AdviceInfo *advice) {
  out << "__" << (advice->name () + 1);
}  

void Naming::call_func (ostream& out, CStructure *in, AdviceInfo *ad) {
   out << "__call_checked_";
   out << in->Name ();
   out << "_";
   out << ad->Scope ()->Name ();
   out << "_";
   out << (ad->name () + 1);
}

void Naming::aspectof(ostream& out, JoinPointLoc *loc, AspectInfo *aspect) {
  out << "::" << aspect->name () << "::aspectof";
}

void Naming::tjp_struct(ostream& out, JoinPointLoc *loc, int depth) {
  out << "TJP_";
  mangle(out, loc->assoc_obj());
  if (loc->lid() >= 0) out << "_" << loc->lid();
  out << "_" << depth;
}

void Naming::tjp_instance(ostream& out, JoinPointLoc *loc) {
//  out << "tjp_";
//  mangle(out, loc->assoc_obj());
//  if (loc->lid() >= 0) out << "_" << loc->lid();
  out << "tjp";
}

void Naming::tjp_args_array(ostream& out, JoinPointLoc *loc) {
  out << "args_";
  mangle(out, loc->assoc_obj());
  if (loc->lid() >= 0) out << "_" << loc->lid();
}

void Naming::tjp_argtypes(ostream& out, JoinPointLoc *loc) {
  out << "argtypes_";
  mangle(out, loc->assoc_obj());
  if (loc->lid() >= 0) out << "_" << loc->lid();
}

void Naming::cflow (ostream& out, AspectInfo *aspect_info, int index) {
  out << "::AC::CFlow<" << aspect_info->name() << "," << index << ">";
}

bool Naming::is_tjp_object (const char *candidate) {
  return strcmp (candidate, "tjp") == 0 ||
    strcmp (candidate, "thisJoinPoint") == 0;
}

void Naming::tjp_typedef (ostream& out, const char *name) {
  out << "__JP_" << name;
}

void Naming::type_check_function (ostream &out, CRecord *in, CRecord *cfor) {
  out << "__ac_";
  scope_name (out, in);
  out << "is";
  scope_name (out, cfor);
}

void Naming::type_check_func (ostream &out, CRecord *in, const string &name) {
  out << "__ac_";
  scope_name (out, in);
  out << "is_" << name;
}

void Naming::guard (ostream &out, FileUnit *unit) {
  out << "__ac_guard_";
  mangle_file (out, unit);
}

void Naming::mangle_file (ostream &out, FileUnit *unit) {
  // TODO: better is unit->absolutePath() but the implementation is strange and
  // has to be changed.
  char *name = FileUnit::absolutePath (unit->Unit::name ());
  mangle_file (out, name);
  if (name)
    delete[] name;
}

void Naming::mangle_file (ostream &out, const char *name) {
  const char *curr = name;
  while (*curr) {
    if (*curr == '_' || isalnum (*curr))
      out << *curr;
    else if (*curr == '/' || *curr == '\\' || *curr == ':' || *curr == '.')
      out << "_";
    else
      out << (int)*curr;
    curr++;
  }
  out << "__";
}

