/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file LICENSE.rst or https://cmake.org/licensing for details.  */
#include "cmCPackLog.h"

#include <iostream>

#include <cm/memory>

#include "cmGeneratedFileStream.h"
#include "cmSystemTools.h"

cmCPackLog::cmCPackLog()
{
  this->DefaultOutput = &std::cout;
  this->DefaultError = &std::cerr;
}

cmCPackLog::~cmCPackLog() = default;

void cmCPackLog::SetLogOutputStream(std::ostream* os)
{
  this->LogOutputStream.reset();
  this->LogOutput = os;
}

bool cmCPackLog::SetLogOutputFile(char const* fname)
{
  this->LogOutputStream.reset();
  if (fname) {
    this->LogOutputStream = cm::make_unique<cmGeneratedFileStream>(fname);
  }
  if (this->LogOutputStream && !*this->LogOutputStream) {
    this->LogOutputStream.reset();
  }

  this->LogOutput = this->LogOutputStream.get();

  return this->LogOutput != nullptr;
}

void cmCPackLog::Log(int tag, char const* file, int line, char const* msg,
                     size_t length)
{
  // By default no logging
  bool display = false;

  // Display file and line number if debug
  bool useFileAndLine = this->Debug;

  bool output = false;
  bool debug = false;
  bool warning = false;
  bool error = false;
  bool verbose = false;

  // When writing in file, add list of tags whenever tag changes.
  std::string tagString;
  bool needTagString = false;
  if (this->LogOutput && this->LastTag != tag) {
    needTagString = true;
  }

  if (tag & LOG_OUTPUT) {
    output = true;
    display = true;
    if (needTagString) {
      if (!tagString.empty()) {
        tagString += ",";
      }
      tagString += "VERBOSE";
    }
  }
  if (tag & LOG_WARNING) {
    warning = true;
    display = true;
    if (needTagString) {
      if (!tagString.empty()) {
        tagString += ",";
      }
      tagString += "WARNING";
    }
  }
  if (tag & LOG_ERROR) {
    error = true;
    display = true;
    if (needTagString) {
      if (!tagString.empty()) {
        tagString += ",";
      }
      tagString += "ERROR";
    }
  }
  if (tag & LOG_DEBUG && this->Debug) {
    debug = true;
    display = true;
    if (needTagString) {
      if (!tagString.empty()) {
        tagString += ",";
      }
      tagString += "DEBUG";
    }
    useFileAndLine = true;
  }
  if (tag & LOG_VERBOSE && this->Verbose) {
    verbose = true;
    display = true;
    if (needTagString) {
      if (!tagString.empty()) {
        tagString += ",";
      }
      tagString += "VERBOSE";
    }
  }
  if (this->Quiet) {
    display = false;
  }
  if (this->LogOutput) {
    if (needTagString) {
      *this->LogOutput << "[" << file << ":" << line << " " << tagString
                       << "] ";
    }
    this->LogOutput->write(msg, length);
  }
  this->LastTag = tag;
  if (!display) {
    return;
  }
  if (this->NewLine) {
    if (error && !this->ErrorPrefix.empty()) {
      *this->DefaultError << this->ErrorPrefix;
    } else if (warning && !this->WarningPrefix.empty()) {
      *this->DefaultError << this->WarningPrefix;
    } else if (output && !this->OutputPrefix.empty()) {
      *this->DefaultOutput << this->OutputPrefix;
    } else if (verbose && !this->VerbosePrefix.empty()) {
      *this->DefaultOutput << this->VerbosePrefix;
    } else if (debug && !this->DebugPrefix.empty()) {
      *this->DefaultOutput << this->DebugPrefix;
    } else if (!this->Prefix.empty()) {
      *this->DefaultOutput << this->Prefix;
    }
    if (useFileAndLine) {
      if (error || warning) {
        *this->DefaultError << file << ":" << line << " ";
      } else {
        *this->DefaultOutput << file << ":" << line << " ";
      }
    }
  }
  if (error || warning) {
    this->DefaultError->write(msg, length);
    this->DefaultError->flush();
  } else {
    this->DefaultOutput->write(msg, length);
    this->DefaultOutput->flush();
  }
  if (msg[length - 1] == '\n' || length > 2) {
    this->NewLine = true;
  }

  if (error) {
    cmSystemTools::SetErrorOccurred();
  }
}
