// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "tools/gn/standard_out.h"

#include <vector>

#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#else
#include <stdio.h>
#include <unistd.h>
#endif

namespace {

bool initialized = false;

static const char kSwitchColor[] = "color";
static const char kSwitchNoColor[] = "nocolor";

#if defined(OS_WIN)
HANDLE hstdout;
WORD default_attributes;
#endif
bool is_console = false;

void EnsureInitialized() {
  if (initialized)
    return;
  initialized = true;

  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
  if (cmdline->HasSwitch(kSwitchNoColor)) {
    // Force color off.
    is_console = false;
    return;
  }

#if defined(OS_WIN)
  // On Windows, we can't force the color on. If the output handle isn't a
  // console, there's nothing we can do about it.
  hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO info;
  is_console = !!::GetConsoleScreenBufferInfo(hstdout, &info);
  default_attributes = info.wAttributes;
#else
  if (cmdline->HasSwitch(kSwitchColor))
    is_console = true;
  else
    is_console = isatty(fileno(stdout));
#endif
}

void WriteToStdOut(const std::string& output) {
  size_t written_bytes = fwrite(output.data(), 1, output.size(), stdout);
  DCHECK_EQ(output.size(), written_bytes);
}

}  // namespace

#if defined(OS_WIN)

void OutputString(const std::string& output, TextDecoration dec) {
  EnsureInitialized();
  if (is_console) {
    switch (dec) {
      case DECORATION_NONE:
        break;
      case DECORATION_DIM:
        ::SetConsoleTextAttribute(hstdout, FOREGROUND_INTENSITY);
        break;
      case DECORATION_RED:
        ::SetConsoleTextAttribute(hstdout,
                                  FOREGROUND_RED | FOREGROUND_INTENSITY);
        break;
      case DECORATION_GREEN:
        // Keep green non-bold.
        ::SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN);
        break;
      case DECORATION_BLUE:
        ::SetConsoleTextAttribute(hstdout,
                                  FOREGROUND_BLUE | FOREGROUND_INTENSITY);
        break;
      case DECORATION_YELLOW:
        ::SetConsoleTextAttribute(hstdout,
                                  FOREGROUND_RED | FOREGROUND_GREEN);
        break;
    }
  }

  DWORD written = 0;
  ::WriteFile(hstdout, output.c_str(), static_cast<DWORD>(output.size()),
              &written, NULL);

  if (is_console)
    ::SetConsoleTextAttribute(hstdout, default_attributes);
}

#else

void OutputString(const std::string& output, TextDecoration dec) {
  EnsureInitialized();
  if (is_console) {
    switch (dec) {
      case DECORATION_NONE:
        break;
      case DECORATION_DIM:
        WriteToStdOut("\e[2m");
        break;
      case DECORATION_RED:
        WriteToStdOut("\e[31m\e[1m");
        break;
      case DECORATION_GREEN:
        WriteToStdOut("\e[32m");
        break;
      case DECORATION_BLUE:
        WriteToStdOut("\e[34m\e[1m");
        break;
      case DECORATION_YELLOW:
        WriteToStdOut("\e[33m\e[1m");
        break;
    }
  }

  WriteToStdOut(output.data());

  if (is_console && dec != DECORATION_NONE)
    WriteToStdOut("\e[0m");
}

#endif

void PrintShortHelp(const std::string& line) {
  size_t colon_offset = line.find(':');
  size_t first_normal = 0;
  if (colon_offset != std::string::npos) {
    OutputString("  " + line.substr(0, colon_offset), DECORATION_YELLOW);
    first_normal = colon_offset;
  }

  // See if the colon is followed by a " [" and if so, dim the contents of [ ].
  if (first_normal > 0 &&
      line.size() > first_normal + 2 &&
      line[first_normal + 1] == ' ' && line[first_normal + 2] == '[') {
    size_t begin_bracket = first_normal + 2;
    OutputString(": ");
    first_normal = line.find(']', begin_bracket);
    if (first_normal == std::string::npos)
      first_normal = line.size();
    else
      first_normal++;
    OutputString(line.substr(begin_bracket, first_normal - begin_bracket),
                 DECORATION_DIM);
  }

  OutputString(line.substr(first_normal) + "\n");
}

void PrintLongHelp(const std::string& text) {
  std::vector<std::string> lines;
  base::SplitStringDontTrim(text, '\n', &lines);

  for (size_t i = 0; i < lines.size(); i++) {
    const std::string& line = lines[i];

    // Check for a heading line.
    if (!line.empty() && line[0] != ' ') {
      // Highlight up to the colon (if any).
      size_t chars_to_highlight = line.find(':');
      if (chars_to_highlight == std::string::npos)
        chars_to_highlight = line.size();
      OutputString(line.substr(0, chars_to_highlight), DECORATION_YELLOW);
      OutputString(line.substr(chars_to_highlight) + "\n");
      continue;
    }

    // Check for a comment.
    TextDecoration dec = DECORATION_NONE;
    for (size_t char_i = 0; char_i < line.size(); char_i++) {
      if (line[char_i] == '#') {
        // Got a comment, draw dimmed.
        dec = DECORATION_DIM;
        break;
      } else if (line[char_i] != ' ') {
        break;
      }
    }

    OutputString(line + "\n", dec);
  }
}