#include "doctest/parts/private/prelude.h"
#include "doctest/parts/private/context_state.h"
#include "doctest/parts/private/ext/windows.h" // IWYU pragma: keep

#ifdef DOCTEST_PLATFORM_WINDOWS
#include <io.h>
#endif

DOCTEST_SUPPRESS_PRIVATE_WARNINGS_PUSH

#if !defined(DOCTEST_CONFIG_COLORS_NONE)
#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI)
#ifdef DOCTEST_PLATFORM_WINDOWS
#define DOCTEST_CONFIG_COLORS_WINDOWS
#else // linux
#define DOCTEST_CONFIG_COLORS_ANSI
#endif // platform
#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI
#endif // DOCTEST_CONFIG_COLORS_NONE

// this is a fix for https://github.com/doctest/doctest/issues/348
// https://mail.gnome.org/archives/xml/2012-January/msg00000.html
#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO)
#define STDOUT_FILENO fileno(stdout)
#endif // HAVE_UNISTD_H

namespace doctest {

namespace detail {
void color_to_stream(std::ostream &, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;)
} // namespace detail

namespace Color {
std::ostream &operator<<(std::ostream &s, Color::Enum code) {
    detail::color_to_stream(s, code);
    return s;
}
} // namespace Color

#ifndef DOCTEST_CONFIG_DISABLE
namespace detail {
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
void color_to_stream(std::ostream &s, Color::Enum code) {
    static_cast<void>(s);    // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
    static_cast<void>(code); // for DOCTEST_CONFIG_COLORS_NONE
#ifdef DOCTEST_CONFIG_COLORS_ANSI
    if (g_no_colors || (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false))
        return;

    auto col = ""; // NOLINT(clang-analyzer-deadcode.DeadStores)
    DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
    DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default")
    switch (code) {
        case Color::Red:         col = "[0;31m"; break;
        case Color::Green:       col = "[0;32m"; break;
        case Color::Blue:        col = "[0;34m"; break;
        case Color::Cyan:        col = "[0;36m"; break;
        case Color::Yellow:      col = "[0;33m"; break;
        case Color::Grey:        col = "[1;30m"; break;
        case Color::LightGrey:   col = "[0;37m"; break;
        case Color::BrightRed:   col = "[1;31m"; break;
        case Color::BrightGreen: col = "[1;32m"; break;
        case Color::BrightWhite: col = "[1;37m"; break;
        case Color::Bright:      // invalid
        case Color::None:
        case Color::White:
        default:                 col = "[0m";
    }
    DOCTEST_CLANG_SUPPRESS_WARNING_POP
    s << "\033" << col;
#endif // DOCTEST_CONFIG_COLORS_ANSI

#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
    if (g_no_colors || (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false))
        return;

    static struct ConsoleHelper {
        HANDLE stdoutHandle;
        WORD origFgAttrs;
        WORD origBgAttrs;

        ConsoleHelper() {
            stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
            GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo);
            origFgAttrs =
                csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
            origBgAttrs =
                csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
        }
    } ch;

#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs)

    // clang-format off
    switch (code) {
        case Color::White:       DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
        case Color::Red:         DOCTEST_SET_ATTR(FOREGROUND_RED);                                      break;
        case Color::Green:       DOCTEST_SET_ATTR(FOREGROUND_GREEN);                                    break;
        case Color::Blue:        DOCTEST_SET_ATTR(FOREGROUND_BLUE);                                     break;
        case Color::Cyan:        DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN);                  break;
        case Color::Yellow:      DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN);                   break;
        case Color::Grey:        DOCTEST_SET_ATTR(0);                                                   break;
        case Color::LightGrey:   DOCTEST_SET_ATTR(FOREGROUND_INTENSITY);                                break;
        case Color::BrightRed:   DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED);               break;
        case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN);             break;
        case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
        case Color::None:
        case Color::Bright:      // invalid
        default:                 DOCTEST_SET_ATTR(ch.origFgAttrs);
    }
        // clang-format on
#endif // DOCTEST_CONFIG_COLORS_WINDOWS
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
} // namespace detail
#endif // DOCTEST_CONFIG_DISABLED
} // namespace doctest

DOCTEST_SUPPRESS_PRIVATE_WARNINGS_POP
