// 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 "Puma/CRecord.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CTemplateInfo.h"
#include "Puma/CTemplateInstance.h"
using namespace Puma;

#include "MatchName.h"
#include "MatchType.h"
#include "MatchTemplateArg.h"

void MatchName::MRegComp::setup (const DString &str) {
  if (_reg_comp)
    delete _reg_comp;
  const char *sig = str.c_str();
  char *buffer = new char[3 + strlen (sig) * 2];
  char *p = buffer;
  char ch;

  *p++ = '^';
  while (*sig) {
    ch = *sig++;
    switch (ch) {
      case '%':
        *p++ = '.';
        *p++ = '*';
        break;
      default:
        *p++ = ch;
    }
  }
  *p++ = '$';
  *p++ = 0;

  _reg_comp = new RegComp (buffer);
  delete[] buffer;
}

bool MatchName::MRegComp::matches (const DString &name) const {
  assert (_reg_comp);
  return _reg_comp->match (name.c_str ());
}

// this is used if a match name component is '%' => always true
bool MatchName::MTrue::matches (const DString &name) const {
  return true;
}

// efficient string comparison
bool MatchName::MStrComp::matches (const DString &name) const {
  return _str == name;
}


void MatchName::Name::make_matcher () {
  // TODO: make more different Matcher variants (prefix/suffix)
  bool only = true; // only '%' characters in the match string
  bool none = true; // no '%' character
  const char *str = _str.c_str ();
  while (*str != '\0') {
    if (*str == '%')
      none = false;
    else
      only = false;
    str++;
  }
  if (only)
    _matcher = new MTrue;
  else if (none)
    _matcher = new MStrComp (_str);
  else
    _matcher = new MRegComp (_str);
}

static CScopeInfo *nscope(CObjectInfo *obj) {
  // template instances are stored in a special scope, better take the template
  if (obj->TemplateInstance ())
    obj = obj->TemplateInstance ()->Template ();

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

MatchName::~MatchName () {
  for (int s = 0; s < scopes (); s++)
    if (_scope_template_args[s])
      delete _scope_template_args[s];
  if (_name_template_args)
    delete _name_template_args;
}

MatchName &MatchName::operator = (const MatchName &copy) {
  _scopes = copy._scopes;
  for (int i = 0; i < copy.scopes (); i++)
    if (copy._scope_template_args[i])
      _scope_template_args.push_back (
        new MatchTemplateArgList (*copy._scope_template_args[i]));
    else
      _scope_template_args.push_back (0);
  _name = copy._name;
  if (copy._name_template_args)
    _name_template_args = new MatchTemplateArgList (*copy._name_template_args);
  else
    _name_template_args = 0;
  _oper = copy._oper;
  _conv_type = copy._conv_type;
  return *this;
}

void MatchName::print (ostream &out) const {
  for (int s = 0; s < scopes (); s++) {
    out << scope (s);
    if (_scope_template_args[s])
      out << *_scope_template_args[s];
    out << "::";
  }
  
  if (!_conv_type.is_undefined ())
    out << "operator " << _conv_type;
  else
    out << name ();
    
  if (_name_template_args)
    out << *_name_template_args;
}

bool MatchName::scope_matches (CScopeInfo *scope, int pos) {

  assert (scopes () > 0);
    
  // check for last scope
  if (pos == -1)
    pos = scopes () - 1;
  
  Name &name = _scopes[pos];
  MatchTemplateArgList *mtal = _scope_template_args[pos];
  pos--;
  
  static DString dots ("...");
  if (name.str () == dots) {
    // if '...' is the start of the qualified name match expression => match
    if (pos == -1)
      return true;
    // handle ellipses (brute force :-( )
    CScopeInfo *curr_scope = scope;
    while (curr_scope) {
      if (scope_matches (curr_scope, pos))
        return true;
      curr_scope = nscope (curr_scope);
    }
    return false;
  }
  
  // if the argument scope is the global scope it is a mismatch
  if (!scope)
    return false;

  // check template arguments of the scope
  if (mtal) {
    CTemplateInstance *instance = scope->TemplateInstance ();
    if (!instance || !mtal->matches (instance))
      return false;
    // the scope is the template, not the instance!
    assert (instance->Template ());
    scope = instance->Template ();
  }
         
  // compare the name pattern
  if (!name.matches (scope->Name ()))
    return false;
  
  CScopeInfo *next_scope = nscope (scope);
    
  // check the next scope
  if (pos >= 0)
    return scope_matches (next_scope, pos);
    
  // no scope to check left, 'scope' must be defined in the global scope
  if (next_scope)
    return false;
    
  // everything ok
  return true;
}

bool MatchName::oper_matches (CFunctionInfo *obj) {
  static DString any ("%");
  
  // '%' as a function name also matches operators
  if (!_name.str ().empty ())
    return _name.str () == any;
      
  // only operator match expressions are considered
  if (_oper == OP_UNDEFINED)
    return false;
    
  // any operator
  if (_oper == OP_ANY)
    return true;
    
  // check the operator name
  static DString obstr[] = {
    DString ("operator +"), DString ("operator -"), DString ("operator *"),
    DString ("operator /"), DString ("operator %"), DString ("operator ^"), 
    DString ("operator &"), DString ("operator |"), DString ("operator ~"), 
    DString ("operator !"), DString ("operator <"), DString ("operator >"),
    DString ("operator =="), DString ("operator >="), DString ("operator <="),
    DString ("operator !="), DString ("operator &&"), DString ("operator ||"), 
    DString ("operator <<"), DString ("operator >>"), DString ("operator --"), 
    DString ("operator ++"), DString ("operator ="), DString ("operator ,"),
    DString ("operator +="), DString ("operator -="), DString ("operator *="),
    DString ("operator /="), DString ("operator %="),
    DString ("operator &="), DString ("operator |="), DString ("operator <<="), 
    DString ("operator >>="), DString ("operator ^="),
    DString ("operator ->*"), DString ("operator ->"), DString ("operator new"),
    DString ("operator delete"), DString ("operator new[]"),
    DString ("operator delete[]"), DString ("operator ()"),
    DString ("operator []")
  };

  return obj->Name () == obstr[((int)_oper) - 1];
}

bool MatchName::conv_matches (CFunctionInfo *obj) {
  static DString any ("%");
  
  // '%' as a function name also matches operators
  if (!_name.str ().empty ())
    return _name.str () == any;
      
  // any operator is ok
  if (_oper == OP_ANY)
    return true;

  // check the conversion type of the function (if it is a conversion function)
  CTypeInfo *ct = obj->ConversionType ();
  return ct && !_conv_type.is_undefined () && _conv_type.matches (ct);
}

bool MatchName::name_matches (CObjectInfo *obj) {
  if (_name_template_args) {
    CTemplateInstance *instance = obj->TemplateInstance ();
    if (!instance || !_name_template_args->matches (instance))
      return false;
  }
  return _name.matches (obj->Name ());
}

bool MatchName::matches (CObjectInfo *obj) {
  // if the match name contains a scope check it first
  CScopeInfo *obj_scope = nscope (obj);
  if (scopes () == 0 && obj_scope)
    return false;
  if (scopes () > 0 && !scope_matches (obj_scope))
    return false;
    
  CFunctionInfo *func = obj->FunctionInfo ();
  if (func && func->isOperator ()) {
    // special treatment for operators
    return oper_matches (func);
  }
  else if (func && func->isConversion ()) {
    // special treatment for conversion functions
    return conv_matches (func);
  }
  else if (_oper == OP_UNDEFINED && _conv_type.is_undefined ()) {
    // normal name
    return name_matches (obj);
  }
  else
    return false;
}

