// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2013  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, 


#ifndef __ClangErrorStream_h__
#define __ClangErrorStream_h__

#include "ACBase/ErrorStream.h"
#include "clang/Frontend/CompilerInstance.h"

#include <iostream>
#include <sstream>

class ClangErrorStream : public ACBase::ErrorStream {
  clang::CompilerInstance *ci_;
  unsigned msg_ids_[5]; // carefull: this must be the number of enumerators in ClangErrorSeverity
  clang::FullSourceLoc full_source_loc_;
public:
  ClangErrorStream () : ci_(nullptr) {}

  void set_compiler_instance (clang::CompilerInstance *ci) {
    ci_ = ci;
    auto &diag = ci->getDiagnostics();
    msg_ids_[ACBase::sev_none] = diag.getCustomDiagID(clang::DiagnosticsEngine::Remark, "%0");
    msg_ids_[ACBase::sev_message] = diag.getCustomDiagID(clang::DiagnosticsEngine::Note, "%0");
    msg_ids_[ACBase::sev_warning] = diag.getCustomDiagID(clang::DiagnosticsEngine::Warning, "%0");
    msg_ids_[ACBase::sev_error] = diag.getCustomDiagID(clang::DiagnosticsEngine::Error, "%0");
    msg_ids_[ACBase::sev_fatal] = diag.getCustomDiagID(clang::DiagnosticsEngine::Fatal, "%0");
  }
  clang::CompilerInstance &get_compiler_instance () const { return *ci_; }

  template<typename T> ClangErrorStream &operator << (T obj) {
    ACBase::ErrorStream::operator<< (obj);
    return *this;
  }

  ClangErrorStream &operator << (clang::SourceLocation l) {
    assert (ci_ && "No CompilerInstance assigned, but SourceLocation used!");
    clang::SourceManager &sm = ci_->getSourceManager();
    full_source_loc_ = clang::FullSourceLoc(l, sm);
    return *this;
  }

  ClangErrorStream &operator << (clang::FullSourceLoc fl) {
    assert (ci_ && "No CompilerInstance assigned, but FullSourceLoc used!");
    full_source_loc_ = fl;
    return *this;
  }

  virtual void output(const std::string &msg) override {
    if (ci_ && ci_->hasDiagnostics()) {
      // if we are already in a context with Clang diagnostics, use it
      auto &diag = ci_->getDiagnostics();
      // save, set, and restore the source manager of the diag engine to fit with the source location
      auto &mgr = diag.getSourceManager();
      diag.setSourceManager(&const_cast<clang::SourceManager&>(full_source_loc_.getManager()));
      diag.Report(full_source_loc_, msg_ids_[message_severity()]) << msg;
      diag.setSourceManager(&mgr);
      full_source_loc_ = clang::FullSourceLoc();
    }
    else {
      ACBase::ErrorStream::output(msg);
    }
  }

};

#endif // __ClangErrorStream_h__
