// 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 "ClangResultBuffer.h"
#include "clang/AST/ASTContext.h"
#include <sstream>
using std::stringstream;

bool ClangResultBuffer::has_constructor_problem () const {
  return _type->isRecordType() && !_type->isReferenceType();
}

bool ClangResultBuffer::workaround_volatile_result() const {
  return _type->isReferenceType() && _type->getPointeeType().isVolatileQualified();
}

string ClangResultBuffer::tjp_result_type () const {
  stringstream out;
  clang::QualType type = _type;
  if (_type->isVoidType()/* || _type->isUndefined ()*/)
    out << "void";
  else if (type->isReferenceType ()) {
    if (_use_typedef) {
      const clang::ReferenceType *ref_type = clang::dyn_cast<clang::ReferenceType>(type);
      if (ref_type->getPointeeType ().isConstQualified ())
        out << "const ";
      out << "TResult";
    }
    else {
      out << TI_Type::get_type_code_text(type.getNonReferenceType(), &_ast_ctx, 0);
    }
  }
  else
    if (_use_typedef) {
      if (type.isConstQualified ())
        out << "const ";
      out << "TResult";
    }
    else
      out << TI_Type::get_type_code_text(type, &_ast_ctx, 0);
  return out.str ();
}

string ClangResultBuffer::deduced_result_type () const {

  // If there is no result type 'void' is the right answer -- even if _use_typedef
  // is true, because the latter doesn't mean that TResult is necessarily defined.
  if (_type->isVoidType()/* || _type->isUndefined ()*/)
    return "void";

    // If we are in a context where TResult is already defined, just use that.
  if (_use_typedef)
    return "TResult";

  // Here we have to simulate the type deduction performed by __AC_TYPEOF(expr)
  stringstream out;
  clang::QualType type = _type;
  if (type->isReferenceType ()) 
    type = type.getNonReferenceType();
  type = type.getLocalUnqualifiedType ();
  out << TI_Type::get_type_code_text(type, &_ast_ctx, 0);
  return out.str ();
}

string ClangResultBuffer::result_type (const string &name, bool unqual) const {
  stringstream out;
  clang::QualType type = _type;
  if (unqual)
    type = type.getLocalUnqualifiedType ();
  if (_type->isVoidType()/* || _type->isUndefined ()*/)
    out << "void";
  else if (type->isReferenceType ()) {
    string ptrname = string ("*") + name;
    if (_use_typedef) {
      const clang::ReferenceType *ref_type = clang::dyn_cast<clang::ReferenceType>(type);
      if (ref_type->getPointeeType ().isConstQualified ())
        out << "const ";
      out << "TResult " << ptrname;
    }
    else {
      out << TI_Type::get_type_code_text(_ast_ctx.getPointerType(type.getNonReferenceType()),
                                    &_ast_ctx,
                                   name.c_str());
    }
  }
  else {
    if (_use_typedef) {
      if (type.isConstQualified())
        out << "const ";
      out << "TResult " << name;
    }
    else {
      out << TI_Type::get_type_code_text(type.getNonReferenceType(), &_ast_ctx, name.c_str());
    }
  }
  return out.str ();
}

string ClangResultBuffer::result_declaration() const {
  stringstream out;
  if (!(_type->isVoidType()/* || _type->isUndefined ()*/)) {
    if (_problem)
      out << "AC::ResultBuffer< " << result_type ("", false) << " > " << result_name();
    else
      out << result_type (result_name(), true);
    out << ";" << endl;
  }
  return out.str ();
}

string ClangResultBuffer::result_assignment(const string &result) const {
  stringstream out;
  if (!(_type->isVoidType()/* || _type->isUndefined ()*/)) {
    // if (_problem) {
    //   out << "::new (&" << result_name() << ") ";
    //   out << result_type ("", true);
    //   out << " (";
    // }
    // else
      out << result_name() << " = ";
   
    if (workaround_volatile_result())
      out << "const_cast<" << result_type( "", false ) <<  ">( ";

    if (_type->isReferenceType ())
      out << "&";
  }
  out << result;
  if (!(_type->isVoidType()/* || _type->isUndefined ()*/)) {
    // if (_problem)
    //   out << ")";
    if (workaround_volatile_result())
      out << " )";
  }
  return out.str ();
}

string ClangResultBuffer::action_result_assignment(const string &result) const {
  stringstream out;
  if (!(_type->isVoidType()/* || _type->isUndefined ()*/)) {
    // if (_problem) {
    //   out << "::new ((AC::ResultBuffer< ";
    //   out << result_type ("", false);
    //   out << " >*)__TJP::result ()) ";
    //   out << result_type ("", false);
    //   out << " (";
    // }
    if (_problem) {
      out << "*((AC::ResultBuffer< ";
      out << result_type ("", false);
      out << " >*)__TJP::result ()) = ";
    }
    else {
      // if( workaround_const_result() )
      //   out << "const_cast<" << result_type( "", false ) <<  " & >( ";
      // out << "*__TJP::result()" << ( workaround_const_result() ? " )" : "" ) << " = (" << result_type( "", false ) << ")";
      out << "*(TResult*";
      if (_type->isReferenceType()) out << "*";
      out << ")__TJP::result() = (TResult";
      if (_type->isReferenceType()) out << "*";
      out << ")";
    }
    if (_type->isReferenceType ())
      out << "&";
  }
  out << result;
  // if (!(_type->isVoidType()/* || _type->isUndefined ()*/))
  //   if (_problem)
  //     out << ")";
  return out.str ();
}

string ClangResultBuffer::result_return() const {
  stringstream out;
  if (!(_type->isVoidType ()/* || _type->isUndefined ()*/)) {
    out << "return ";
    if (_type->isReferenceType ())
      out << "*";
    out << "(";
    out << result_type ("&");
    out << ")" << result_name() << ";" << endl;
  }
  return out.str ();
}
