//===- TextDiagnosticPrinter.cpp ------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/LD/TextDiagnosticPrinter.h>
#include <mcld/LinkerConfig.h>
#include <llvm/Support/Signals.h>
#include <string>

using namespace mcld;

static const enum llvm::raw_ostream::Colors UnreachableColor = llvm::raw_ostream::RED;
static const enum llvm::raw_ostream::Colors FatalColor       = llvm::raw_ostream::YELLOW;
static const enum llvm::raw_ostream::Colors ErrorColor       = llvm::raw_ostream::RED;
static const enum llvm::raw_ostream::Colors WarningColor     = llvm::raw_ostream::MAGENTA;
static const enum llvm::raw_ostream::Colors DebugColor       = llvm::raw_ostream::CYAN;
static const enum llvm::raw_ostream::Colors NoteColor        = llvm::raw_ostream::GREEN;
static const enum llvm::raw_ostream::Colors IgnoreColor      = llvm::raw_ostream::BLUE;

// Used for changing only the bold attribute.
static const enum llvm::raw_ostream::Colors SavedColor = llvm::raw_ostream::SAVEDCOLOR;

//===----------------------------------------------------------------------===//
// TextDiagnosticPrinter
TextDiagnosticPrinter::TextDiagnosticPrinter(llvm::raw_ostream& pOStream,
                                             const LinkerConfig& pConfig)
  : m_OStream(pOStream), m_Config(pConfig), m_pInput(NULL) {
}

TextDiagnosticPrinter::~TextDiagnosticPrinter()
{
}

/// HandleDiagnostic - Handle this diagnostic, reporting it to the user or
/// capturing it to a log as needed.
void
TextDiagnosticPrinter::handleDiagnostic(DiagnosticEngine::Severity pSeverity,
                                        const Diagnostic& pInfo)
{
  DiagnosticPrinter::handleDiagnostic(pSeverity, pInfo);

  std::string out_string;
  pInfo.format(out_string);

  switch (pSeverity) {
    case DiagnosticEngine::Unreachable: {
      m_OStream.changeColor(UnreachableColor, true);
      m_OStream << "Unreachable: ";
      m_OStream.resetColor();
      m_OStream << out_string << "\n";
      break;
    }
    case DiagnosticEngine::Fatal: {
      m_OStream.changeColor(FatalColor, true);
      m_OStream << "Fatal: ";
      m_OStream.resetColor();
      m_OStream << out_string << "\n";
      break;
    }
    case DiagnosticEngine::Error: {
      m_OStream.changeColor(ErrorColor, true);
      m_OStream << "Error: ";
      m_OStream.resetColor();
      m_OStream << out_string << "\n";
      break;
    }
    case DiagnosticEngine::Warning: {
      m_OStream.changeColor(WarningColor, true);
      m_OStream << "Warning: ";
      m_OStream.resetColor();
      m_OStream << out_string << "\n";
      break;
    }
    case DiagnosticEngine::Debug: {
      // show debug message only if verbose >= 0
      if (0 <= m_Config.options().verbose()) {
        m_OStream.changeColor(DebugColor, true);
        m_OStream << "Debug: ";
        m_OStream.resetColor();
        m_OStream << out_string << "\n";
      }
      break;
    }
    case DiagnosticEngine::Note: {
      // show ignored message only if verbose >= 1
      if (1 <= m_Config.options().verbose()) {
        m_OStream.changeColor(NoteColor, true);
        m_OStream << "Note: ";
        m_OStream.resetColor();
        m_OStream << out_string << "\n";
      }
      break;
    }
    case DiagnosticEngine::Ignore: {
      // show ignored message only if verbose >= 2
      if (2 <= m_Config.options().verbose()) {
        m_OStream.changeColor(IgnoreColor, true);
        m_OStream << "Ignore: ";
        m_OStream.resetColor();
        m_OStream << out_string << "\n";
      }
      break;
    }
    default:
      break;
  }

  switch (pSeverity) {
    case DiagnosticEngine::Unreachable: {
      m_OStream << "\n\n";
      m_OStream.changeColor(llvm::raw_ostream::YELLOW);
      m_OStream << "You encounter a bug of MCLinker, please report to:\n"
                << "  mclinker@googlegroups.com\n";
      m_OStream.resetColor();
    }
    /** fall through **/
    case DiagnosticEngine::Fatal: {
      // If we reached here, we are failing ungracefully. Run the interrupt handlers
      // to make sure any special cleanups get done, in particular that we remove
      // files registered with RemoveFileOnSignal.
      llvm::sys::RunInterruptHandlers();
      exit(1);
      break;
    }
    case DiagnosticEngine::Error: {
      int16_t error_limit = m_Config.options().maxErrorNum();
      if ((error_limit != -1) &&
          (getNumErrors() > static_cast<unsigned>(error_limit))) {
        m_OStream << "\n\n";
        m_OStream.changeColor(llvm::raw_ostream::YELLOW);
        m_OStream << "too many error messages (>" << error_limit << ")...\n";
        m_OStream.resetColor();
        llvm::sys::RunInterruptHandlers();
        exit(1);
      }
      break;
    }
    case DiagnosticEngine::Warning: {
      int16_t warning_limit = m_Config.options().maxWarnNum();
      if ((warning_limit != -1) &&
          (getNumWarnings() > static_cast<unsigned>(warning_limit))) {
        m_OStream << "\n\n";
        m_OStream.changeColor(llvm::raw_ostream::YELLOW);
        m_OStream << "too many warning messages (>" << warning_limit << ")...\n";
        m_OStream.resetColor();
        llvm::sys::RunInterruptHandlers();
        exit(1);
      }
    }
    default:
      break;
  }
}

void TextDiagnosticPrinter::beginInput(const Input& pInput, const LinkerConfig& pConfig)
{
  m_pInput = &pInput;
}

void TextDiagnosticPrinter::endInput()
{
  m_pInput = NULL;
}