// 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 "IntroductionInfo.h"

#include "Puma/ACTree.h"
#include "Puma/ACIntroductionInfo.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CArgumentInfo.h"
#include "Puma/CClassDatabase.h"

IntroductionInfo::IntroductionInfo (AspectInfo *ai, ACIntroductionInfo *acii) :
  _aspect (ai), _acii(acii), _tree (acii->def_node ()), _prot (acii->prot ()) {

  _type = INTRO_ERROR;
  if (_tree->Decl ()->NodeName () == CT_Intro::NodeId ()) {
    // Copy all tokens of the intro pattern => intro source code can be removed
    // The pattern contains no whitespace or any kind of formatting!
    // Token positions are identical with the entry number.
    CT_Intro *intro = (CT_Intro*)_tree->Decl ();
    // set the name for #line directives
    _pattern.name (((Unit*)intro->token ()->belonging_to ())->name ());
    for (int e = 0; e < intro->Entries (); e++) {
      Token *tok = intro->Entry (e)->token ();
      _pattern.append (*tok->duplicate ());
    }
    _type = INTRO_OTHER;
  }
  else if (_tree->Decl ()->NodeName () == CT_SliceRef::NodeId ()) {
    _type = INTRO_SLICE_REF;
    CObjectInfo *obj = ((CT_SliceRef*)_tree->Decl ())->name ()->Object ();
    ACSliceInfo *acsi = obj->ClassDB ()->SliceInfo (obj)->definition ();
    if (acsi) {
      if (acsi->type () == ACSliceInfo::SL_STRUCT)
        _prot = CProtection::PROT_PUBLIC;
      else
        _prot = CProtection::PROT_PRIVATE;
      _objs.append (acsi->object ());
      CT_ClassSliceDecl *csd = acsi->def_node ();
      if (csd->members ()) {
        for (int m = 0; m < csd->members ()->Entries (); m++) {
          // set the name for #line directives
          _pattern.name (((Unit*)csd->token ()->belonging_to ())->name ());
          CT_Intro *intro = (CT_Intro*)csd->members ()->Entry (m);
          for (int e = 0; e < intro->Entries (); e++) {
            Token *tok = intro->Entry (e)->token ();
            _pattern.append (*tok->duplicate ());
          }
        }
      }
    }
    else
      _objs.append (obj); // append the declaration, error message later
  }
  else if (_tree->Decl ()->NodeName () == CT_ClassSliceDecl::NodeId ()) {
    _type = INTRO_SLICE_DECL;
    CObjectInfo *obj = ((CT_ClassSliceDecl*)_tree->Decl ())->Object ();
    ACSliceInfo *acsi = obj->ClassDB ()->SliceInfo (obj)->definition ();
    if (acsi) {
      if (acsi->type () == ACSliceInfo::SL_STRUCT)
        _prot = CProtection::PROT_PUBLIC;
      else
        _prot = CProtection::PROT_PRIVATE;
      _objs.append (acsi->object ());
      CT_ClassSliceDecl *csd = acsi->def_node ();
      if (csd->members ()) {
        // set the name for #line directives
        _pattern.name (((Unit*)csd->token ()->belonging_to ())->name ());
        for (int m = 0; m < csd->members ()->Entries (); m++) {
          CT_Intro *intro = (CT_Intro*)csd->members ()->Entry (m);
          for (int e = 0; e < intro->Entries (); e++) {
            Token *tok = intro->Entry (e)->token ();
            _pattern.append (*tok->duplicate ());
          }
        }
      }
    }
  }
  else if (_tree->Decl ()->NodeName () == CT_ObjDecl::NodeId ()) {
    CT_DeclaratorList *dl = ((CT_ObjDecl*)_tree->Decl ())->Declarators();
    if (dl->Entries () > 0) {
      CObjectInfo *obj = ((CT_InitDeclarator*)dl->Entry (0))->Object ();
      
      // check for baseclass introductions
      if (obj->FunctionInfo () && strcmp (obj->Name (), "baseclass") == 0) {
      	_type = INTRO_BASECLASS;
      	_objs.append (obj);
      }
      else if (obj->FunctionInfo () &&
	             strcmp (obj->Name (), "inherited") == 0) {
        _type = INTRO_INHERITED;
        _objs.append (obj);
      }
    }
  }
}


void IntroductionInfo::print (ostream& os) const {
  switch (_type) {
  case INTRO_BASECLASS:
    os << "baseclass ";
    object(0)->FunctionInfo ()->Argument (0u)->TypeInfo ()->print (os);
    break;
  case INTRO_INHERITED:
    os << "inherited";
    break;
  case INTRO_SLICE_REF:
    os << "slice ref " << object (0)->QualName ();
    break;
  case INTRO_SLICE_DECL:
    os << "slice decl " << object (0)->QualName ();
    break;
  case INTRO_OTHER: {
    CT_Intro *intro = (CT_Intro*)_tree->Decl ();
    bool is_fct = (intro->Entry (intro->Entries () - 1)->
      token ()->type () == TOK_CLOSE_CURLY);
    for (int i = 0; i < intro->Entries (); i++) {
      Token *tok = intro->Entry (i)->token ();
      if (is_fct && tok->type () == TOK_OPEN_CURLY) {
        os << "{ ... }";
        break;
      }
      os << tok->text () << " ";
    }
  }
    break;
  default:
    os << "unknown";
  }
}
