// see License.txt for copyright and terms of use

#include "squash.h"        // this module
#include "squash_cmd.h"    // SquashCmd
#include "squash_global.h"
#include "oink.gr.gen.h"   // CCParse_Oink
#include "squash_min_max_line_visitor.h"
#include "squash_patcher.h"
#include "squash_expr_rewriter.h"
#include "squash_outparam_remover.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <set>
#include <algorithm>
#include <map>
#include <list>

class RewriteVisitor : public ASTVisitor {
public:
  RewriteVisitor(SquashCmd &cmd,
                 Patcher &patcher);

  virtual bool visitTypeSpecifier(TypeSpecifier *obj);
  virtual bool visitFunction(Function *obj);
  virtual bool visitDeclarator(Declarator *obj);
  virtual bool visitTopForm(TopForm *obj);
  virtual void postvisitMember(Member *obj);

private:
  void rewriteCtorDtor(PQ_name *name);
  void listTypes(Type* t, bool isPointer,
                 std::set<std::string> &instanceTypes,
                 std::set<std::string> &pointerTypes,
                 std::map<std::string,
                 std::string> &includes);

  void rewriteType(Type *t,
                   std::set<std::string> &instanceTypes);
  void rewriteAtomicType(AtomicType *t,
                         std::set<std::string> &instanceTypes);
  void patchHeaders(str_map const &headers,
                    std::vector<std::string> const &missingClasses,
                    std::vector<std::string> const &forwards);
  void patchDependantEntities(ostream &ostream);
  bool isRewriteStage();

  void mergeInterfaceImplementation(TS_classSpec *interfaceClass, 
                                    TS_classSpec *implementationClass);

  void addMemberDeclarations(TS_classSpec *c, ASTList<Member> &implMembers,
                                 std::stringstream& ss, MinMaxLineVisitor &mmlv,
                                 std::set<std::string> const &ifacePointerTypes,
                                 std::set<std::string> &ifaceInstanceTypes);

  void correctHeaders(std::set<std::string> const &ifacePointerTypes,
                      std::set<std::string> const &ifaceInstanceTypes,
                      std::set<std::string> const &implPointerTypes,
                      std::set<std::string> const &implInstanceTypes);
  void rewriteFunctionBody(Function *f);
  void rewriteFunction(Function *f, bool processBodyOnly,
                       std::string const &currentFile);

private:
  std::string implementationClassName;
  std::string implementationHeaderFile;
  // on the second pass these will contain the interface name & definition file
  std::string interfaceClassName;
  std::string interfaceFile;
  std::string interfaceDtor;
  // keep these around to know what to replace with implementation
  TS_classSpec* interfaceClass;
  unsigned int lastLine;
  
  //entities that may need to be moved into the interface
  std::map<std::string, TypeSpecifier*> implEntities;
  SquashCmd &cmd;
  Patcher &patcher;
  //the int indicates order of output
  std::map<int, TypeSpecifier*> requiredToplevel;
  OutparamRemover outparamRemover;
  // mapping for filename to includes
  str_map includes;
  str_map simpleTypedefs;
};

void Squash::printRewrite_stage(SquashCmd &cmd) {
  printStage("printRewrite");
  Restorer<bool> restorer(value2typeIsOn, true);
  Patcher patcher(cmd);
  foreachSourceFile {
    File *file = files.data();
    std::string filename = file->name;
    if(filename.empty() || filename[0] != '/') {
      cerr << "Absolute filenames are required. Rejected: " << filename<< endl;
      exit(1);
    }
    cerr << "Processing " <<  filename << endl;
    maybeSetInputLangFromSuffix(file);
    TranslationUnit *unit = file2unit.get(file);
    patcher.setFile(filename);
    RewriteVisitor vis(cmd, patcher);
    // During the first traversal find out the base class
    unit->traverse(vis);
    if(!cmd.squash) continue;
    // On the second traversal merge the subclass members
    // into the base class. Remove the subclass when done.
    unit->traverse(vis);
  }
}

static void killImplicitMembers(TS_classSpec *c) {
  ASTListMutator<Member> m(c->members->list);
  while(!m.isDone()) {
    Member *mem = m.data();

    if (!mem->isMR_func()) {
      m.adv();
      continue;
    }

    Function *f = mem->asMR_func()->f;
    if (f->dflags & DF_IMPLICIT) {
      m.remove();
    } else {
      m.adv();
    }
  }
}

/** If m1 is a pure
 *   virtual declaration for m2 then return true. */
static bool sameMember(Member *m1, Member *m2) {
  if (!(m1->isMR_decl() && m2->isMR_decl())) return false;

  Declaration* dn1 = m1->asMR_decl()->d;
  Declaration* dn2 = m2->asMR_decl()->d;
  FakeList<Declarator>* d1 = dn1->decllist;
  FakeList<Declarator>* d2 = dn2->decllist;

  if (d1->isEmpty() ||
     d2->isEmpty()) {
    return false;
  }

  xassert(d1->butFirst()->isEmpty());
  xassert(d2->butFirst()->isEmpty());

  Type* t1 = d1->first()->type;
  Type* t2 = d2->first()->type;
  Variable* v1 = d1->first()->var;
  Variable* v2 = d2->first()->var;

  if (! (v1->name == v2->name &&
        t1->isFunctionType() &&
        t2->isFunctionType() &&
        v1->isPureVirtualMethod())) {
    return false;
  }

  FunctionType const *f1 = t1->asFunctionTypeC();
  FunctionType const *f2 = t2->asFunctionTypeC();
  SObjListIter<Variable> it1(f1->params);
  SObjListIter<Variable> it2(f2->params);

  if (! (f1->retType->equals(f1->retType) &&
        f1->params.count() == f2->params.count() &&
        !it1.isDone())) {
    return false;
  }

  // The first argument, 'this', of a different type by definition
  it1.adv();
  it2.adv();

  for (; !it1.isDone();
       it1.adv(), it2.adv()) {
    if (!it1.data()->type->equals(it2.data()->type)) {
      return false;
    }
  }

  return true;
}

RewriteVisitor::RewriteVisitor(SquashCmd &cmd,
                              Patcher &patcher) :
     implementationClassName(cmd.className),
     interfaceClass(NULL),
     lastLine(0),
     cmd(cmd),
     patcher(patcher),
     outparamRemover(cmd, patcher)
{
}

void RewriteVisitor::rewriteCtorDtor(PQ_name *pq) {
  if (implementationClassName == pq->name) {
    pq->name = interfaceClassName.c_str();
  } else if (strlen(pq->name) && pq->name[0] == '~'
             && implementationClassName == pq->name + 1)
  {
    pq->name = interfaceDtor.c_str();
  }
}

void RewriteVisitor::rewriteType(Type *type,
                                 std::set<std::string> &instanceTypes)
{
  AtomicType_list als;
  listAtomicTypes(type, als);
  for (AtomicType_list::iterator i = als.begin(); i != als.end(); ++i) {
    AtomicType *t = *i;
    std::string name;
    if (t->isCompoundType()) {
      CompoundType* c = t->asCompoundType();
      if (c->keyword ==CompoundType:: K_CLASS &&
          implementationClassName == c->instName &&
          c->typedefVar && implementationClassName == c->typedefVar->name)
      {
        c->typedefVar->name = c->instName = interfaceClassName.c_str();
        continue;
      }
      name = c->instName;
    } else if (t->isEnumType()) {
      name = t->asEnumType()->name;
    } else {
      continue;
    }
    std::map<std::string, TypeSpecifier*>::iterator it =
      implEntities.find(name);
    if (it == implEntities.end()) continue;

    instanceTypes.insert(name);

    TypeSpecifier *ts = it->second;
    MinMaxLineVisitor mmlv = traverseAndGuess(patcher, ts);
    requiredToplevel[sourceLocManager->getLine(ts->loc)] = ts;
    // now that the element has been defined, this prevents it from
    // being defined again
    patcher.printPatch("", mmlv, implementationHeaderFile);
    implEntities.erase(it);
  }
}

bool RewriteVisitor::isRewriteStage() {
  return !interfaceClassName.empty() && interfaceClass != NULL;
}

static void recordHeaders(TS_classSpec const *c,
                          std::set<std::string> &instanceTypes,
                          str_map &includes)
{
  if (!c) return;
  const std::string name = c->name->getName();
  if (!c || instanceTypes.find(name) != instanceTypes.end()) return;

  instanceTypes.insert(name);
  includes[name] = sourceLocManager->getFile(c->loc);
  for (BaseClassSpec *parent = c->bases->first();parent != NULL;
       parent = parent->next)
  {
    if (parent->type && parent->type->syntax) {
      recordHeaders(parent->type->syntax, instanceTypes, includes);
    }
  }
}

// Figures out types that need to be includes or forward declared and
// collects include info
void RewriteVisitor::listTypes(Type* t, bool isPointer,
                               std::set<std::string> &instanceTypes,
                               std::set<std::string> &pointerTypes,
                               str_map &includes)
{
  xassert(! t->isPointerToMemberType());
  if (t->isPtrOrRef()) {
    listTypes(t->getAtType(), true, instanceTypes, pointerTypes, includes);
  } else if (t->isArrayType()) {
    listTypes(t->asArrayTypeC()->eltType, isPointer, instanceTypes,
              pointerTypes, includes);
  } else if (t->isFunctionType()) {
    FunctionType const *f = t->asFunctionTypeC();
    listTypes(f->retType, f->retType->isPtrOrRef(), instanceTypes,
              pointerTypes, includes);
    SObjListIter<Variable> it(f->params);
    //skip 'this'
    if (!it.isDone()) it.adv();
    
    for (; !it.isDone(); it.adv()) {
      Type *pt = it.data()->type;
      listTypes(pt, pt->isPtrOrRef(), instanceTypes, pointerTypes,
                includes);
    }
  } else if (t->isCompoundType()) {
    CompoundType const *c = t->asCompoundTypeC();
    // FIX: put template itself into forward decls too
    TemplateInfo *ti = c->templateInfo();
    recordHeaders(c->syntax, instanceTypes, includes);
    // if not a template can record class name
    if (!ti) {
      if (isPointer) pointerTypes.insert(c->instName);
      return;
    }

    ObjListIter<STemplateArgument> it(ti->arguments);
    for (;!it.isDone();it.adv()) {
      const STemplateArgument *ta = it.data();
      if (ta->isType()) {
        listTypes(ta->getType(), true, instanceTypes, pointerTypes, includes);
      }
    }
  }
}

void RewriteVisitor::patchHeaders
(str_map const &headers,
 std::vector<std::string> const &missingClasses,
 std::vector<std::string> const &forwards)
{
  if (0 == missingClasses.size() + forwards.size() + cmd.includes.size()) {
    return;
  }
  
  MinMaxLineVisitor mmlv;
  unsigned int limit = 1 + patcher.lineCount(interfaceFile);
  std::string orig;
  bool includeSection = false;
  for (mmlv.minLine = 1; mmlv.minLine < limit; ++mmlv.minLine) {
    orig = patcher.getLine(mmlv.minLine, interfaceFile);
    if (orig.find("#include ") != std::string::npos) {
      includeSection = true;
    } else if (includeSection) break;
  }
  mmlv.maxLine = mmlv.minLine;
  // to handle the case when there are no headers included
  // which would presumably be the only safe line to replace -> 1
  if (mmlv.maxLine == limit) {
    mmlv.maxLine = mmlv.minLine = 1;
    orig = patcher.getLine(mmlv.minLine, interfaceFile);
  }
  std::string const prefix="dist/include/";
  std::stringstream ss;
  // deal with duplicates
  str_map uniqueHeaders;
  for (std::vector<std::string>::const_iterator i = cmd.includes.begin();
        i != cmd.includes.end(); i++) {
    ss << "#include \""<< *i << "\" // cmd-line\n";
  }
  
  for (std::vector<std::string>::const_iterator i=missingClasses.begin();
        i!=missingClasses.end(); ++i) {
     std::string header = headers.find(*i)->second;
     std::string::size_type pos = header.find(prefix);
     if (pos != std::string::npos) {
       header = header.substr(pos + prefix.size(),
                              header.size() - prefix.size() - pos);
     }
     if (cmd.excludes.find(header) == cmd.excludes.end()) {
       uniqueHeaders[header] += " " + *i;
     }
  }
  for (str_map::const_iterator i = uniqueHeaders.begin();
       i != uniqueHeaders.end();
       i++)
  {
    ss << "#include \"" << i->first << "\" //" << i->second << "\n";
  }
  
  for (std::vector<std::string>::const_iterator i = forwards.begin();
       i!=forwards.end();
       ++i)
  {
    ss << "class " << *i<< ";\n";
  }
  ss << orig << "\n";
  patcher.printPatch(ss.str(), mmlv, interfaceFile);
}

// this adds classes that should be defined before the squashed inface class
void RewriteVisitor::patchDependantEntities(ostream &ostream) {
  for (std::map<int, TypeSpecifier*>::const_iterator i=requiredToplevel.begin();
       i!=requiredToplevel.end();
       ++i)
  {
    TypeSpecifier *ts = i->second;
    if (ts->isTS_classSpec()) {
      killImplicitMembers(ts->asTS_classSpec());
      print(ostream, ts);
      ostream << ";\n";
    } else {
      patcher.copy(traverseAndGuess(patcher, ts), implementationHeaderFile,
                   ostream, "");
    }
  }
}

// implementationClass can be null if no squashing is to be done
void RewriteVisitor::mergeInterfaceImplementation
(TS_classSpec *interfaceClass,
 TS_classSpec *implementationClass)
{
  MinMaxLineVisitor mmlv;
  std::stringstream ss;
  std::set<std::string> ifacePointerTypes;
  std::set<std::string> ifaceInstanceTypes;
  ifaceInstanceTypes.insert(interfaceClassName);
  ifaceInstanceTypes.insert(implementationClassName);
  recordHeaders(interfaceClass, ifaceInstanceTypes, includes);
  
  // not really needed, but in case things change this ensures that we
  // never look at implicit members
  killImplicitMembers(interfaceClass);

  ASTList<Member> implMembers;
  if (implementationClass) {
    implMembers.appendAll(implementationClass->members->list);
  }
  for (ASTListIterNC<Member> interfaceMemberIt(interfaceClass->members->list);
      !interfaceMemberIt.isDone();
      interfaceMemberIt.adv()) {
    str_map::const_iterator it = cmd.memberRenames.end();
    Member *ifaceMember = interfaceMemberIt.data();
    if (ifaceMember->isMR_func()) {
      Function *f = ifaceMember->asMR_func()->f;
      PQName *n = f->nameAndParams->getDeclaratorId();
      // skip weird functions like operators
      if(!n->isPQ_name()) continue;
      PQ_name *pqname = n->asPQ_name();
      it = cmd.memberRenames.find(pqname->name);
      // probably should queue the body rewrite until
      // the iface class definition is rewritten
      if (it != cmd.memberRenames.end()) {
        MinMaxLineVisitor mmlv;
        pqname->traverse(mmlv);
        patcher.replaceWord(mmlv, it->first, it->second);
      }
      visitFunction(f);
      continue;
    }
    else if (!ifaceMember->isMR_decl()) continue;

    if (!ss.str().empty()) {
      patcher.printPatch(ss.str(), mmlv, interfaceFile);
      ss.str("");
      mmlv.reset();
    }

    Declarator *d = ifaceMember->asMR_decl()->d->decllist->first();
    if (d) {
      listTypes(d->var->type, d->var->type->isPtrOrRef(), ifaceInstanceTypes,
                ifacePointerTypes, includes);
      it = cmd.memberRenames.find(d->var->name);
    }

    // if we are doing a plain rename then there will be no squashing
    // (that is, the inner loop wont happen)
    if (!implementationClass && it != cmd.memberRenames.end()) {
      PQName *n = d->getDeclaratorId();
      MinMaxLineVisitor mmlv;
      n->traverse(mmlv);
      patcher.replaceWord(mmlv, it->first, it->second);
      ifaceMember->traverse(mmlv);
    } else if(implementationClass) {
      ifaceMember->traverse(mmlv);
    }

    for (ASTListMutator<Member> implMemberIt(implMembers);
         !implMemberIt.isDone();
	 implMemberIt.adv())
    {
      Member* implMember = implMemberIt.data();
      // need a proper comparison
      if (sameMember(ifaceMember, implMember)) {
        outparamRemover.postvisitMember(implMember);
        Declaration *d = implMember->ifMR_decl()->d;
        Variable *v = d->decllist->first()->var;
        // remove virtual attribute unless told otherwise
        if (cmd.virtuals.find(v->name) == cmd.virtuals.end()) {
          // not really sure why there are 2 sets of virtual flags
          v->clearFlag(DF_VIRTUAL);
	  d->dflags &= ~DF_VIRTUAL;
        }
        if (it != cmd.memberRenames.end()) {
          PQName *n =   d->decllist->first()->getDeclaratorId();
          xassert(n->isPQ_name());
          n->asPQ_name()->name = it->second.c_str();
        }

        print(ss, implMemberIt.remove());
        break;
      }
    }
  }

  if (!implementationClass) return;
  addMemberDeclarations(interfaceClass, implMembers, ss, mmlv,
			ifacePointerTypes, ifaceInstanceTypes);
  // remove the implementation class
  patcher.printPatch("", traverseAndGuess(patcher, implementationClass),
                     implementationHeaderFile);
  
  // add in dependencies just above top of the interface class
  ss.str("");
  patchDependantEntities(ss);
  mmlv.maxLine = mmlv.minLine = sourceLocManager->getLine(interfaceClass->loc);
  ss << patcher.getLine(mmlv.minLine, interfaceFile) << "\n";
  patcher.printPatch(ss.str(), mmlv, interfaceFile);
}

void RewriteVisitor::addMemberDeclarations
(TS_classSpec *c,
 ASTList<Member> &implMembers,
 std::stringstream& ss, MinMaxLineVisitor &mmlv,
 std::set<std::string> const &ifacePointerTypes,
 std::set<std::string> &ifaceInstanceTypes)
{
  std::set<std::string> implPointerTypes;
  std::set<std::string> implInstanceTypes;
   // now printout the remaining members
  for (ASTListIterNC<Member> implMemberIt(implMembers);
       ! implMemberIt.isDone();
       implMemberIt.adv())
  {
    Member *m(implMemberIt.data());
    MR_decl *mr(m->isMR_decl() ? m->asMR_decl() : NULL);

    if (!mr && !m->isMR_access()) {
      continue;
    }

    if (mr && mr->d->decllist->isNotEmpty()) {
      Declarator *d = mr->d->decllist->first();
      Variable *v = d->var;
      // dsw: FIX: there is a faster way to test if v is a constructor
      if (!streq(v->name, "constructor-special")) {
        rewriteType(v->type, ifaceInstanceTypes);
        listTypes(v->type, v->type->isPtrOrRef(), implInstanceTypes,
                  implPointerTypes, includes);
      }
      rewriteCtorDtor(d->getDeclaratorId()->asPQ_name());
    }

    print(ss, m);
  }
  patcher.printPatch(ss.str(), mmlv, interfaceFile);

  correctHeaders(ifacePointerTypes, ifaceInstanceTypes,
		 implPointerTypes, implInstanceTypes);
}


void RewriteVisitor::correctHeaders
(std::set<std::string> const &ifacePointerTypes,
 std::set<std::string> const &ifaceInstanceTypes,
 std::set<std::string> const &implPointerTypes,
 std::set<std::string> const &implInstanceTypes)
{
  std::vector<std::string> missingForwardsInterim;
  std::vector<std::string> missingForwards;
  std::vector<std::string> missingIncludes;

  std::set_difference(implPointerTypes.begin(), implPointerTypes.end(),
                      ifacePointerTypes.begin(), ifacePointerTypes.end(),
                      back_inserter(missingForwardsInterim));

  std::set_difference(missingForwardsInterim.begin(),
                      missingForwardsInterim.end(),
                      ifaceInstanceTypes.begin(),
                      ifaceInstanceTypes.end(),
                      back_inserter(missingForwards));

  std::set_difference(implInstanceTypes.begin(),
                      implInstanceTypes.end(),
                      ifaceInstanceTypes.begin(),
                      ifaceInstanceTypes.end(),
                      back_inserter(missingIncludes));
  patchHeaders(includes, missingIncludes, missingForwards);
}

// finds impl & interface classes and controls the rewrite process
bool RewriteVisitor::visitTypeSpecifier(TypeSpecifier *obj) {
  TS_classSpec *c = obj->isTS_classSpec() ? obj->asTS_classSpec() : NULL;
  // record entities which may need to be moved up into the interface class
  if (implementationHeaderFile == sourceLocManager->getFile(obj->loc)
      && !(c && implementationClassName == c->name->getName())
      && !(c && interfaceClassName == c->name->getName())
      && (obj->isTS_classSpec() || obj->isTS_enumSpec()))
  {
    std::string const name = obj->isTS_classSpec()
      ? obj->asTS_classSpec()->name->getName()
      : obj->asTS_enumSpec()->name;
    implEntities[name] = obj;
  }

  if (!c || !c->name) return true;

  std::string const className = c->name->getName();
  // record derivatives of targeted classes as targeted classes
  for (FakeList<BaseClassSpec > *bs = c->bases;
       !bs->isEmpty();
       bs = bs->butFirst())
  {
    BaseClassSpec *base = bs->first();
    if (!base->name) continue;
    std::string name = base->name->getName();
    str_map::iterator it = simpleTypedefs.find(name);
    if (it != simpleTypedefs.end()) name = it->second;

    if (cmd.targetClasses.find(name) != cmd.targetClasses.end()) {
      cmd.targetClasses.insert(className);
      break;
    }
  }
  // On the first pass we are looking for the implementation class and
  // also figuring out what it derives from.
  if (cmd.squash) {
    if (interfaceClassName.empty()) {
      if (implementationClassName != className) {
        return false;
      }
      if (c->bases->first()) {
        interfaceClassName = c->bases->first()->name->getName();
        if (interfaceClassName == "nsISupports") {
          cerr << implementationClassName
	       << " is an interface, perhaps you meant -sq-no-squash"
	       << endl;
          exit(1);
        }
        implementationHeaderFile = sourceLocManager->getFile(obj->loc);
        interfaceDtor = '~' + interfaceClassName;
      } else {
        cerr << implementationClassName
	     << " does not derive from anything"
	     << endl;
        exit(1);
      }
      return false;
    } else if (interfaceClassName == className) {
      interfaceFile = sourceLocManager->getFile(obj->loc);
      interfaceClass = c;
      // traverse impl class to remove existing members
      return false;
    }
    // if this is not the second pass or any of above other cases then
    // we don't care
    else if (implementationClassName != className) {
      return true;
    }
    // if we made it to this point then we are in the second pass
    killImplicitMembers(c);
    mergeInterfaceImplementation(interfaceClass, c);
    return false;
  } else if (cmd.targetClasses.find(className) != cmd.targetClasses.end()) {
    // fake everything; this code makes code makes even less sense now
    // that inhertiance is supported; TODO: make this sensible
    interfaceClassName = implementationClassName = className;
    interfaceFile = sourceLocManager->getFile(c->loc);
    if (cmd.rewriteDeclaration) {
      mergeInterfaceImplementation(c, NULL);
    }
  }
  return true;
}

void RewriteVisitor::rewriteFunctionBody(Function *f) {
  ExprRewriter er(interfaceClassName, implementationClassName, patcher, cmd);
  er.rewrite(f->body);
}

void RewriteVisitor::rewriteFunction(Function *f, bool processBodyOnly,
                                    std::string const &currentFile)
{
  f->inits = FakeList<MemberInit>::emptyList( );
  // capture the function name part of the function definition
  MinMaxLineVisitor mmlv, mmlv_body, mmlv_params;
  f->retspec->traverse(mmlv);
  f->nameAndParams->decl->traverse(mmlv_params);
  mmlv.maxLine = mmlv_params.maxLine;

  f->body->traverse(mmlv_body);

  // HEURISTIC: sometimes MACROs define multiple functions per line.
  // Skip them.
  if (mmlv_body.maxLine == lastLine) {
    return;
  } else {
    lastLine = mmlv_body.maxLine;
  }

  rewriteFunctionBody(f);

  if (processBodyOnly) return;
  
  std::stringstream ss;
  print(ss, f);

  std::string str = ss.str();
  std::string const orig = patcher.getLine(mmlv.maxLine, currentFile);
  if (mmlv_body.maxLine > mmlv.minLine 
      || f->body->asS_compound()->stmts.isEmpty()) {
    std::string::size_type pos = str.find('{');
    str[pos] = '\n';
    str = str.substr(0, pos+1);
    // if the original file contained '{' on the same line as func
    // name then append that and whatever came after it to our string
    pos = orig.find("{");
    if (pos != std::string::npos) {
      str = str.substr(0, str.size() - 1) + orig.substr(pos) + "\n";
    }
  } else {
    // HEURISTIC: Function is on a line of it's own imples it is MACRO
    std::string implementationClassName = this->implementationClassName;
    str = patcher.getLine(mmlv.minLine, currentFile);
    std::string::size_type pos = str.find(implementationClassName);
    if (pos != std::string::npos) {
      str = str.replace(pos, implementationClassName.size(),
                        interfaceClassName) + "\n";
    } else {
      // this is a macro that has no interesting keywords in it
      // therefore leave it as is
      return;
    }
  }
  patcher.printPatch(str, mmlv, currentFile);
}

// FIX: this needs to be cleaned-up
bool RewriteVisitor::visitFunction(Function *f) {
  // verify that this is the second traversal
  if (cmd.squash && !isRewriteStage()) return false;
  outparamRemover.rewriteFunction(f);
  PQName  *pqname = f->nameAndParams->getDeclaratorId();
  std::string const currentFile = sourceLocManager->getFile(pqname->loc);
  if (currentFile.empty() || currentFile[0] != '/') return false;

  bool processBodyOnly = !pqname->isPQ_qualifier() 
    || !cmd.rewriteDeclaration;

  ExprRewriter er(interfaceClassName, implementationClassName,
		  patcher, cmd);
  for (FakeList <MemberInit >* inits = f->inits; !inits->isEmpty();
      inits = inits->butFirst())
  {
    er.rewriteMemberInit(inits->first());
  }
  if (!processBodyOnly) {
    PQ_qualifier *q = pqname->asPQ_qualifier();

    // if this is is related to the requiredToplevel i iterator will
    // be set to the corresponding iterator pos
    std::map<int, TypeSpecifier*>::const_iterator i;
    for (i = requiredToplevel.begin();
	 i != requiredToplevel.end();
	 ++i)
    {
      TypeSpecifier *ts = i->second;
      if (!ts->isTS_classSpec()) continue;
      TS_classSpec *c = ts->asTS_classSpec();
      if (c->name && streq(c->name->getName(), q->qualifier)) break;
    }

    PQName *rest = q->rest;
    if (implementationClassName == q->qualifier && rest->isPQ_name()) {
      processBodyOnly = true;
      PQ_name *pq = rest->asPQ_name();
      str_map::const_iterator it =
	cmd.memberRenames.find(pq->name);
      if (it != cmd.memberRenames.end()) {
        pq->name = it->second.c_str();
        processBodyOnly = false;
      }

      if (cmd.squash) {
        q->qualifier = interfaceClassName.c_str();
        rewriteCtorDtor(pq);
        processBodyOnly = false;
      }
    } else if (i == requiredToplevel.end()) {
      processBodyOnly = true;
    } 
  }
  rewriteFunction(f, processBodyOnly, currentFile);
  return false;
}

/* declarators are like
 * static class CSSParserImpl *CSSParserImpl::t = NULL
 */
bool RewriteVisitor::visitDeclarator(Declarator *obj) {
  if (!isRewriteStage()) return true;

  PQName *pqname = obj->getDeclaratorId();
  if (!pqname || !pqname->isPQ_qualifier()) return true;
  PQ_qualifier *qualifier = pqname->asPQ_qualifier();

  if (implementationClassName != qualifier->qualifier) return true;
  qualifier->qualifier = interfaceClassName.c_str();

  MinMaxLineVisitor mmlv;
  obj->traverse(mmlv);
  std::stringstream ss;
  print(ss, obj);
  ss << ";\n";

  // FIX: remove this workaround once pretty printing fixes are in
  std::string str = ss.str();
  std::string const needle = "static ";
  std::string::size_type pos = str.find(needle);
  if (pos != std::string::npos) {
    str.replace(pos, needle.size(), "");
  }
  patcher.printPatch(str, mmlv, sourceLocManager->getFile(pqname->loc));
  return true;
}

bool RewriteVisitor::visitTopForm(TopForm *obj) {
  if (!obj->isTF_decl()) return true;
  Declaration *d = obj->asTF_decl()->decl;
  FakeList<Declarator> *dls = d->decllist;
  if (d->dflags != DF_TYPEDEF || !d->spec || !d->spec->isTS_name()
     || !dls || !dls->butFirst()->isEmpty())
  {
    return true;
  }
  std::string to = d->spec->asTS_name()->name->getName();
  IDeclarator* decl = dls->first()->decl;
  if (!decl->isD_name()) return true;

  std::string from = decl->asD_name()->name->getName();
  // flatten the hiearchy
  str_map::const_iterator it = simpleTypedefs.find(to);
  if (it != simpleTypedefs.end()) to = it->second;
  simpleTypedefs[from] = to;
  return true;
}

void RewriteVisitor::postvisitMember(Member *obj) {
  outparamRemover.postvisitMember(obj);
}
