// This file is part of PUMA.
// Copyright (C) 1999-2003  The PUMA developer team.
//                                                                
// 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 <iostream>
using namespace std;

#include "Puma/CTree.h"
#include "Puma/CObjectInfo.h"
#include "Puma/ErrorStream.h"
#include "Puma/TokenStream.h"
#include "Puma/TokenProvider.h"
#include "Puma/CCSyntax.h"
#include "Puma/CCParser.h"
#include "Puma/PreprocessorParser.h"

#include "Introducer.h"
#include "ACUnit.h"
#include "Plan.h"
#include "CodeWeaver.h"
#include "LineDirectiveMgr.h"

// called when a new class/union/struct/aspect is created, current scope
// is the global scope
void Introducer::class_before (CT_ClassDef *cd) {
  enter ();
  assert (cd);
  
//  ErrorStream &err = cd->Object ()->ClassDB ()->Project ()->err ();
//
//  ACUnit *include_unit = new ACUnit (err);
//  include_unit->name ("includes for intro4711");
//  *include_unit << endl
//        << "#ifndef XX" << endl
//        << "#define XX YY" << endl
//        << "#endif" << endl
//        << "extern int XX;" << endu;
//        
//  // add the unit the the translation unit's local unit manager
//  _cpp->locals ()->addUnit (include_unit);
//  
//  // let the normal preprocessor process the token sequence
//  _cpp->scanner ()->push (include_unit);
//
//  ACUnit *unit = new ACUnit (err);
//  unit->name ("intro4711");
//  Token *t;
//  do {
//    t = _cpp->next ();
//    unit->append (*(Token*)t->duplicate ());
//  } while (t && t != include_unit->last ());
//
//  // add the unit the the translation unit's local unit manager
//  _cpp->locals ()->addUnit (unit);
//
//  // parse the preprocessed code as global declarations
//  TokenStream stream;
//  stream.push (unit);
//  TokenProvider provider (stream);   // look ahead token buffer
//  CTree *tree = _parser.syntax ().run (provider, &CCSyntax::decl_seq);
//  cd->add_intro (tree);
  
  leave ();
}

// called when a new class/union/struct/aspect is created
void Introducer::class_begin (CT_ClassDef *cd) {
  enter ();
  assert (cd);
  leave ();
}


// called when a new class/union/struct/aspect definition ends
// (still in the class scope)
void Introducer::class_end (CT_ClassDef *cd, CTree *close) {
  enter ();
  assert (cd);

  CClassInfo *ci = (CClassInfo*)cd->Object ()->DefObject ();
  ErrorStream &err = ci->ClassDB ()->Project ()->err ();

  // first check with the plan if there are intros for this class
  JPP_Class *jp_plan = plan_lookup (ci);
  if (!jp_plan) {
    leave ();
    return;
  }
  
  // create a unit with the code that shall be introduced
  LineDirectiveMgr &lmgr = _code_weaver.line_directive_mgr ();
  Unit *unit = jp_plan->gen_intro (err, ci, lmgr);
  
  // parse the introduced code as a member
  TokenStream stream;
  stream.push (unit);
  TokenProvider provider (stream);   // look ahead token buffer
  CTree *tree = _parser.syntax ().run (provider, &CCSyntax::member_spec); 
  if (tree) {
    assert (tree->NodeName () == CT_MembList::NodeId ());
    cd->IntroMembers (tree);
    // manipulate the code (will be effective after commit),
    // paste declaration before "}" of the class definition
    Token *inspos = close->token ();
    const WeavePos &pos = _code_weaver.weave_pos (inspos, WeavePos::WP_BEFORE);
    // paste the generated code and add the unit to the manipulator's unit mgr.
    _code_weaver.paste (pos, unit);
    // paste a #line directive
    ACUnit *dunit = new ACUnit (err);
    lmgr.directive (*dunit, (Unit*)inspos->belonging_to (), inspos);
    *dunit << endu;
    if (dunit->empty ()) delete dunit; else _code_weaver.paste (pos, dunit);
  }
  
  leave ();
}


// manage the intro nesting level and the _cpp pointer
void Introducer::enter () {
  if (_intro_level == 0)
    _cpp = &(PreprocessorParser&)_parser.syntax ().provider ()->source ();
  _intro_level++;
}
void Introducer::leave () {
  _intro_level--;
  if (_intro_level == 0)
    _cpp = 0;
}

JPP_Class *Introducer::plan_lookup (CClassInfo *ci) {
  for (int i = 0; i < _plan.class_jp_plans (); i++) {
    JPP_Class &jp_plan = _plan.class_jp_plan (i);
    JPL_Class &jp_loc  = _plan.class_jp_loc (i);
    if (strcmp (jp_loc.class_info ()->QualName (), ci->QualName ()) == 0)
      return &jp_plan;
  }
  return 0;
}
