// 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>
using std::string;
#include <sstream>
using std::ostringstream;
using std::ends;

#include "Puma/CRecord.h"
#include "Puma/CEnumInfo.h"
using namespace Puma;

#include "MatchTypeInfos.h"

// comparison functions
  
// is it the same kind of type?
bool MatchType::operator ==(const MatchType &other) const {
  // by default two types are equal if they have the same ID
  return type () == other.type ();
}

MTUndefined *MTUndefined::clone () const {
  return new MTUndefined(*this);
}

void MTUndefined::print (ostream &out, const char *inner, bool prefix) const {
  out << "<undefined>";
  if (inner)
    out << " " << inner;
}

bool MTUndefined::matches (CTypeInfo *ctype) const {
  // conversion operator might have an undefined result type
  return ctype->isUndefined ();
}

// print the string representation of this qualified type
void MTQual::print_qual (ostream &out) const {
  if (_const) out << "const";
  if (_const && _volatile) out << " ";
  if (_volatile) out << "volatile";
}

// perform a type match
// if a type is qualified as const/volatile in a match expression, it does
// only match types which are qualified like this. However, according to
// the AspectC++ semantics a type is also matched if it is const/volatile,
// but the match expression does not require this qualifier.
bool MTQual::qual_matches (CTypeQualified *qtype) const {
  if (!qtype && is_qualified ())
    return false;
  if (qtype) {
    if (_const && !qtype->isConst ())
      return false;
    if (_volatile && !qtype->isVolatile ())
      return false;
  }
  return true;
}

// print the string representation of this primitive type
// inner might be a name, prefix == true means that the name has prefix
void MTPrim::print (ostream &out, const char *inner, bool prefix) const {
  if (is_qualified ()) {
    print_qual (out);
    out << " ";
  }
  out << type ();
  if (inner)
    out << " " << inner;
}

MTNamed *MTNamed::clone () const {
  return new MTNamed (*this);
}

// print the string representation of this names type
// inner might be a name, prefix == true means that the name has prefix
void MTNamed::print (ostream &out, const char *inner, bool prefix) const {
  if (is_qualified ()) {
    print_qual (out);
    out << " ";
  }
  out << _name;
  if (inner)
    out << " " << inner;
}

bool MTNamed::matches (CTypeInfo *ctype) const {
  CTypeQualified *qtype = ctype->TypeQualified ();
  if (qtype)
    ctype = qtype->BaseType ();
  CTypeRecord *rctype = ctype->TypeRecord ();
  if (rctype)
    return _name.matches (rctype->Record ()) && qual_matches (qtype);
  CTypeEnum *ectype = ctype->TypeEnum ();
  return ectype && _name.matches (ectype->EnumInfo ()) && qual_matches (qtype); 
}


MTPointer *MTPointer::clone () const {
  return new MTPointer (*this);
}

// print the string represenation of this pointer type
// inner might be a name, prefix == true means that the name has prefix
void MTPointer::print (ostream &out, const char *inner, bool prefix) const {
  ostringstream declarator;
  declarator << "*";
  if (is_qualified ()) {
    declarator << " ";
    print_qual (declarator);
    declarator << " ";
  }
  if (inner)
    declarator << inner;
  declarator << ends;
  if (base ())
    base ()->print (out, declarator.str ().c_str (), true);
  else
    out << "<any> " << declarator.str ();
}

bool MTPointer::matches (CTypeInfo *ctype) const {
  CTypeQualified *qtype = ctype->TypeQualified ();
  CTypePointer *pctype = (qtype ? qtype->BaseType () : ctype)->TypePointer ();
  return pctype && base ()->matches (pctype->BaseType ()) &&
         qual_matches (qtype);
}


MTMembPointer *MTMembPointer::clone () const {
  return new MTMembPointer (*this);
}

// print the string represenation of this pointer type
// inner might be a name, prefix == true means that the name has prefix
void MTMembPointer::print (ostream &out, const char *inner, bool prefix) const {
  ostringstream declarator;
  declarator << _memb_ptr_scope << "*";
  if (is_qualified ()) {
    declarator << " ";
    print_qual (declarator);
    declarator << " ";
  }
  if (inner)
    declarator << inner;
  declarator << ends;
  if (base ())
    base ()->print (out, declarator.str ().c_str (), true);
  else
    out << "<any> " << declarator.str ();
}

bool MTMembPointer::matches (CTypeInfo *ctype) const {
  CTypeQualified *qtype = ctype->TypeQualified ();
  CTypeMemberPointer *mpctype =
    (qtype ? qtype->BaseType () : ctype)->TypeMemberPointer ();
  return mpctype && base ()->matches (mpctype->BaseType ()) &&
         qual_matches (qtype) && 
         _memb_ptr_scope.scope_matches (mpctype->Record ());
}

MTReference *MTReference::clone () const {
  return new MTReference (*this);
}

// print the string represenation of this reference type
// inner might be a name, prefix == true means that the name has prefix
void MTReference::print (ostream &out, const char *inner, bool prefix) const {
  ostringstream declarator;
  declarator << "&";
  if (inner)
    declarator << inner;
  declarator << ends;
  if (base ())
    base ()->print (out, declarator.str ().c_str (), true);
  else
    out << "<any> " << declarator.str ();
}

bool MTReference::matches (CTypeInfo *ctype) const {
  return ctype->TypeAddress () && base ()->matches (ctype->BaseType ());
}

//MTFunction::~MTFunction () {
//  for (int a = 0; a < args (); a++)
//    delete arg (a);
//}

MTFunction *MTFunction::clone () const {
  return new MTFunction (*this);
}

// print the string represenation of this function type
// inner might be a name, prefix == true means that the name has prefix
void MTFunction::print (ostream &out, const char *inner, bool prefix) const {
  ostringstream declarator;
  if (inner)
    declarator << (prefix ? "(" : "") << inner << (prefix ? ")" : "");
  declarator << "(";
  int a = 0;
  while (a < args ()) {
    if (a > 0) declarator << ",";
    arg (a).type_info ()->print (declarator);
    a++;    
  }
  if (_varargs) declarator << (a > 0 ? "," : "") << "...";
  declarator << ")";
  if (is_qualified ()) {
    declarator << " ";
    print_qual (declarator);
    declarator << " ";
  }
  declarator << ends;
  if (base ())
    base ()->print (out, declarator.str ().c_str (), false);
  else
    out << "<any> " << declarator.str ();
}

// perform a type match
bool MTFunction::matches (CTypeInfo *ctype) const {
  // check if it's a function type
  CTypeFunction *cftype = ctype->TypeFunction ();
  if (!cftype)
    return false;
  // check the type qualifier
  if (!qual_matches (cftype))
    return false;
  // check the argument types
  int cftype_args = (int)cftype->ArgTypes ()->Entries ();
  if ((!_varargs && args () != cftype_args) || (args () > cftype_args))
    return false;
  for (int a = 0; a < args (); a++)
    if (!arg (a).matches (cftype->ArgTypes ()->Entry ((unsigned)a)))
      return false;
  // finally check the result type
  return base ()->matches (cftype->ReturnType ());  
}

// adjust the argument types according to �8.3.5.2 and �8.3.5.3 of ISO C++
void MTFunction::adjust_args (bool &f, bool &a, bool &q, bool &v) {
  // adjust all argument types according to ISO C++
  for (int i = 0; i < args (); i++) {
    MatchType *curr = _arg_types[i].type_info ();
    
    // recursively adjust the argument
    curr->adjust_args (f, a , q, v);
    
    // type `function returning T' is adjusted to be
    // `pointer to function returning T'
    if (_arg_types[i].is_function ()) {
      f = true;
      _arg_types[i].to_pointer ();
      continue;
    }
    
    // type `array of T' is adjusted to be `pointer to T'
    if (_arg_types[i].is_array ()) {
      a = true;
      MatchTypeRef converted_type = _arg_types[i].base ();
      converted_type.to_pointer();
      _arg_types[i] = converted_type;
      continue;
    }
    
    // any cv-qualifier modifying a parameter is deleted
    if (_arg_types[i].is_qualifiable () &&
        _arg_types[i].qualifiers ().is_qualified ()) {
      q = true;
      _arg_types[i].qualifiers ().unqualify ();
    }
  }
  
  // �8.3.5.2 the parameter list `(void)' is equivalent to
  // the empty parameter list
  if (args () == 1 && _arg_types[0].is_void ()) {
    v = true;
    _arg_types.clear ();
  }
}

MTArray *MTArray::clone () const {
  return new MTArray (*this);
}

// print the string represenation of this array type
// inner might be a name, prefix == true means that the name has prefix
void MTArray::print (ostream &out, const char *inner, bool prefix) const {
  ostringstream declarator;
  if (inner)
    declarator << (prefix ? "(" : "") << inner << (prefix ? ")" : "");
  declarator << "[";
  if (_any_size)
    declarator << "%";
  else
    declarator << _dim;
  declarator << "]" << ends;
  if (base ())
    base ()->print (out, declarator.str ().c_str (), false);
  else
    out << "<any> " << declarator.str ();
}

// perform a type match
bool MTArray::matches (CTypeInfo *ctype) const {
  CTypeArray *actype = ctype->TypeArray ();
  return actype && base ()->matches (ctype->BaseType ()) &&
         (_any_size || actype->Dimension () == (long)_dim);
}

MTAny *MTAny::clone () const {
  return new MTAny (*this);
}
// perform a type match
bool MTAny::matches (CTypeInfo *ctype) const {
  return qual_matches (ctype->TypeQualified ());
}

MTBool *MTBool::clone () const {
  return new MTBool (*this);
}

// perform a type match
bool MTBool::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_bool () && qual_matches (ctype->TypeQualified ());
}

MTSignedChar *MTSignedChar::clone () const {
  return new MTSignedChar (*this);
}
// perform a type match
bool MTSignedChar::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_signed_char () && qual_matches (ctype->TypeQualified ());
}

MTUnsignedChar *MTUnsignedChar::clone () const {
  return new MTUnsignedChar (*this);
}
// perform a type match
bool MTUnsignedChar::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_unsigned_char () && qual_matches (ctype->TypeQualified ());
}

MTChar *MTChar::clone () const {
  return new MTChar (*this);
}
// perform a type match
bool MTChar::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_char () && qual_matches (ctype->TypeQualified ());
}

MTUnsignedShort *MTUnsignedShort::clone () const {
  return new MTUnsignedShort (*this);
}
// perform a type match
bool MTUnsignedShort::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_unsigned_short () && qual_matches (ctype->TypeQualified ());
}

MTShort *MTShort::clone () const {
  return new MTShort (*this);
}
// perform a type match
bool MTShort::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_short () && qual_matches (ctype->TypeQualified ());
}

MTUnsignedInt *MTUnsignedInt::clone () const {
  return new MTUnsignedInt (*this);
}
// perform a type match
bool MTUnsignedInt::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_unsigned_int () && qual_matches (ctype->TypeQualified ());
}

MTInt *MTInt::clone () const {
  return new MTInt (*this);
}
// perform a type match
bool MTInt::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_int () && qual_matches (ctype->TypeQualified ());
}

MTWCharT *MTWCharT::clone () const {
  return new MTWCharT (*this);
}
// perform a type match
bool MTWCharT::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_wchar_t () && qual_matches (ctype->TypeQualified ());
}

MTUnsignedLong *MTUnsignedLong::clone () const {
  return new MTUnsignedLong (*this);
}
// perform a type match
bool MTUnsignedLong::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_unsigned_long () && qual_matches (ctype->TypeQualified ());
}

MTLong *MTLong::clone () const {
  return new MTLong (*this);
}
// perform a type match
bool MTLong::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_long () && qual_matches (ctype->TypeQualified ());
}

MTUnsignedLongLong *MTUnsignedLongLong::clone () const {
  return new MTUnsignedLongLong (*this);
}
// perform a type match
bool MTUnsignedLongLong::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_unsigned_long_long () && qual_matches (ctype->TypeQualified ());
}

MTLongLong *MTLongLong::clone () const {
  return new MTLongLong (*this);
}
// perform a type match
bool MTLongLong::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_long_long () && qual_matches (ctype->TypeQualified ());
}

MTFloat *MTFloat::clone () const {
  return new MTFloat (*this);
}
// perform a type match
bool MTFloat::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_float () && qual_matches (ctype->TypeQualified ());
}

MTDouble *MTDouble::clone () const {
  return new MTDouble (*this);
}
// perform a type match
bool MTDouble::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_double () && qual_matches (ctype->TypeQualified ());
}

MTLongDouble *MTLongDouble::clone () const {
  return new MTLongDouble (*this);
}
// perform a type match
bool MTLongDouble::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_long_double () && qual_matches (ctype->TypeQualified ());
}

MTVoid *MTVoid::clone () const {
  return new MTVoid (*this);
}
// perform a type match
bool MTVoid::matches (CTypeInfo *ctype) const {
  return ctype->UnqualType ()->is_void () && qual_matches (ctype->TypeQualified ());
}

// type ids
MatchType::TID MTUndefined::_tid = "<undefined>";
MatchType::TID MTUndefined::type () const { return _tid; }
MatchType::TID MTDeclarator::_tid = "<declarator>";
MatchType::TID MTDeclarator::type () const { return _tid; }
MatchType::TID MTReference::_tid = "&";
MatchType::TID MTReference::type () const { return _tid; }
MatchType::TID MTPointer::_tid = "*";
MatchType::TID MTPointer::type () const { return _tid; }
MatchType::TID MTMembPointer::_tid = "::*";
MatchType::TID MTMembPointer::type () const { return _tid; }
MatchType::TID MTFunction::_tid = "()";
MatchType::TID MTFunction::type () const { return _tid; }
MatchType::TID MTArray::_tid = "[]";
MatchType::TID MTArray::type () const { return _tid; }
MatchType::TID MTAny::_tid = "%";
MatchType::TID MTAny::type () const { return _tid; }
MatchType::TID MTNamed::_tid = "<named>";
MatchType::TID MTNamed::type () const { return _tid; }
MatchType::TID MTBool::_tid = "bool";
MatchType::TID MTBool::type () const { return _tid; }
MatchType::TID MTSignedChar::_tid = "signed char";
MatchType::TID MTSignedChar::type () const { return _tid; }
MatchType::TID MTUnsignedChar::_tid = "unsigned char";
MatchType::TID MTUnsignedChar::type () const { return _tid; }
MatchType::TID MTChar::_tid = "char";
MatchType::TID MTChar::type () const { return _tid; }
MatchType::TID MTUnsignedShort::_tid = "unsigned short";
MatchType::TID MTUnsignedShort::type () const { return _tid; }
MatchType::TID MTShort::_tid = "short";
MatchType::TID MTShort::type () const { return _tid; }
MatchType::TID MTUnsignedInt::_tid = "unsigned int";
MatchType::TID MTUnsignedInt::type () const { return _tid; }
MatchType::TID MTInt::_tid = "int";
MatchType::TID MTInt::type () const { return _tid; }
MatchType::TID MTWCharT::_tid = "wchar_t";
MatchType::TID MTWCharT::type () const { return _tid; }
MatchType::TID MTUnsignedLong::_tid = "unsigned long";
MatchType::TID MTUnsignedLong::type () const { return _tid; }
MatchType::TID MTLong::_tid = "long";
MatchType::TID MTLong::type () const { return _tid; }
MatchType::TID MTUnsignedLongLong::_tid = "unsigned long long";
MatchType::TID MTUnsignedLongLong::type () const { return _tid; }
MatchType::TID MTLongLong::_tid = "long long";
MatchType::TID MTLongLong::type () const { return _tid; }
MatchType::TID MTFloat::_tid = "float";
MatchType::TID MTFloat::type () const { return _tid; }
MatchType::TID MTDouble::_tid = "double";
MatchType::TID MTDouble::type () const { return _tid; }
MatchType::TID MTLongDouble::_tid = "long double";
MatchType::TID MTLongDouble::type () const { return _tid; }
MatchType::TID MTVoid::_tid = "void";
MatchType::TID MTVoid::type () const { return _tid; }
