// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "precomp.h"
#include "settings.hpp"

#include "..\interactivity\inc\ServiceLocator.hpp"
#include "../types/inc/utils.hpp"

#pragma hdrstop

#define DEFAULT_NUMBER_OF_COMMANDS 25
#define DEFAULT_NUMBER_OF_BUFFERS 4

using Microsoft::Console::Interactivity::ServiceLocator;

Settings::Settings() :
    _dwHotKey(0),
    _dwStartupFlags(0),
    _wFillAttribute(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE), // White (not bright) on black by default
    _wPopupFillAttribute(FOREGROUND_RED | FOREGROUND_BLUE | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY), // Purple on white (bright) by default
    _wShowWindow(SW_SHOWNORMAL),
    _wReserved(0),
    // dwScreenBufferSize initialized below
    // dwWindowSize initialized below
    // dwWindowOrigin initialized below
    _nFont(0),
    // dwFontSize initialized below
    _uFontFamily(0),
    _uFontWeight(0),
    // FaceName initialized below
    _uCursorSize(CURSOR_SMALL_SIZE),
    _bFullScreen(false),
    _bQuickEdit(true),
    _bInsertMode(true),
    _bAutoPosition(true),
    _uHistoryBufferSize(DEFAULT_NUMBER_OF_COMMANDS),
    _uNumberOfHistoryBuffers(DEFAULT_NUMBER_OF_BUFFERS),
    _bHistoryNoDup(false),
    // ColorTable initialized below
    _uCodePage(ServiceLocator::LocateGlobals().uiOEMCP),
    _uScrollScale(1),
    _bLineSelection(true),
    _bWrapText(true),
    _fCtrlKeyShortcutsDisabled(false),
    _bWindowAlpha(BYTE_MAX), // 255 alpha = opaque. 0 = transparent.
    _fFilterOnPaste(false),
    _fTrimLeadingZeros(FALSE),
    _fEnableColorSelection(FALSE),
    _fAllowAltF4Close(true),
    _dwVirtTermLevel(0),
    _fUseWindowSizePixels(false),
    _fAutoReturnOnNewline(true), // the historic Windows behavior defaults this to on.
    _fRenderGridWorldwide(false), // historically grid lines were only rendered in DBCS codepages, so this is false by default unless otherwise specified.
    // window size pixels initialized below
    _fInterceptCopyPaste(0),
    _DefaultForeground(INVALID_COLOR),
    _DefaultBackground(INVALID_COLOR),
    _fUseDx(false),
    _fCopyColor(false)
{
    _dwScreenBufferSize.X = 80;
    _dwScreenBufferSize.Y = 25;

    _dwWindowSize.X = _dwScreenBufferSize.X;
    _dwWindowSize.Y = _dwScreenBufferSize.Y;

    _dwWindowSizePixels = { 0 };

    _dwWindowOrigin.X = 0;
    _dwWindowOrigin.Y = 0;

    _dwFontSize.X = 0;
    _dwFontSize.Y = 16;

    ZeroMemory((void*)&_FaceName, sizeof(_FaceName));
    wcscpy_s(_FaceName, DEFAULT_TT_FONT_FACENAME);

    ZeroMemory((void*)&_LaunchFaceName, sizeof(_LaunchFaceName));

    _CursorColor = Cursor::s_InvertCursorColor;
    _CursorType = CursorType::Legacy;

    gsl::span<COLORREF> tableView = { _ColorTable, gsl::narrow<ptrdiff_t>(COLOR_TABLE_SIZE) };
    gsl::span<COLORREF> xtermTableView = { _XtermColorTable, gsl::narrow<ptrdiff_t>(XTERM_COLOR_TABLE_SIZE) };
    ::Microsoft::Console::Utils::Initialize256ColorTable(xtermTableView);
    ::Microsoft::Console::Utils::InitializeCampbellColorTableForConhost(tableView);
}

// Routine Description:
// - Applies hardcoded default settings that are in line with what is defined
//   in our Windows edition manifest (living in win32k-settings.man).
// - NOTE: This exists in case we cannot access the registry on desktop platforms.
//   We will use this to provide better defaults than the constructor values which
//   are optimized for OneCore.
// Arguments:
// - <none>
// Return Value:
// - <none> - Adjusts internal state only.
void Settings::ApplyDesktopSpecificDefaults()
{
    _dwFontSize.X = 0;
    _dwFontSize.Y = 16;
    _uFontFamily = 0;
    _dwScreenBufferSize.X = 120;
    _dwScreenBufferSize.Y = 9001;
    _uCursorSize = 25;
    _dwWindowSize.X = 120;
    _dwWindowSize.Y = 30;
    _wFillAttribute = 0x7;
    _wPopupFillAttribute = 0xf5;
    wcscpy_s(_FaceName, L"__DefaultTTFont__");
    _uFontWeight = 0;
    _bInsertMode = TRUE;
    _bFullScreen = FALSE;
    _fCtrlKeyShortcutsDisabled = false;
    _bWrapText = true;
    _bLineSelection = TRUE;
    _bWindowAlpha = 255;
    _fFilterOnPaste = TRUE;
    _bQuickEdit = TRUE;
    _uHistoryBufferSize = 50;
    _uNumberOfHistoryBuffers = 4;
    _bHistoryNoDup = FALSE;

    gsl::span<COLORREF> tableView = { _ColorTable, gsl::narrow<ptrdiff_t>(COLOR_TABLE_SIZE) };
    ::Microsoft::Console::Utils::InitializeCampbellColorTableForConhost(tableView);

    _fTrimLeadingZeros = false;
    _fEnableColorSelection = false;
    _uScrollScale = 1;
}

void Settings::ApplyStartupInfo(const Settings* const pStartupSettings)
{
    const DWORD dwFlags = pStartupSettings->_dwStartupFlags;

    // See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx

    // Note: These attributes do not get sent to us if we started conhost
    //      directly.  See minkernel/console/client/dllinit for the
    //      initialization of these values for cmdline applications.

    if (WI_IsFlagSet(dwFlags, STARTF_USECOUNTCHARS))
    {
        _dwScreenBufferSize = pStartupSettings->_dwScreenBufferSize;
    }

    if (WI_IsFlagSet(dwFlags, STARTF_USESIZE))
    {
        // WARNING: This size is in pixels when passed in the create process call.
        // It will need to be divided by the font size before use.
        // All other Window Size values (from registry/shortcuts) are stored in characters.
        _dwWindowSizePixels = pStartupSettings->_dwWindowSize;
        _fUseWindowSizePixels = true;
    }

    if (WI_IsFlagSet(dwFlags, STARTF_USEPOSITION))
    {
        _dwWindowOrigin = pStartupSettings->_dwWindowOrigin;
        _bAutoPosition = FALSE;
    }

    if (WI_IsFlagSet(dwFlags, STARTF_USEFILLATTRIBUTE))
    {
        _wFillAttribute = pStartupSettings->_wFillAttribute;
    }

    if (WI_IsFlagSet(dwFlags, STARTF_USESHOWWINDOW))
    {
        _wShowWindow = pStartupSettings->_wShowWindow;
    }
}

// Method Description:
// - Applies settings passed on the commandline to this settings structure.
//      Currently, the only settings that can be passed on the commandline are
//      the initial width and height of the screenbuffer/viewport.
// Arguments:
// - consoleArgs: A reference to a parsed command-line args object.
// Return Value:
// - <none>
void Settings::ApplyCommandlineArguments(const ConsoleArguments& consoleArgs)
{
    const short width = consoleArgs.GetWidth();
    const short height = consoleArgs.GetHeight();

    if (width > 0 && height > 0)
    {
        _dwScreenBufferSize.X = width;
        _dwWindowSize.X = width;

        _dwScreenBufferSize.Y = height;
        _dwWindowSize.Y = height;
    }
    else if (ServiceLocator::LocateGlobals().getConsoleInformation().IsInVtIoMode())
    {
        // If we're a PTY but we weren't explicitly told a size, use the window size as the buffer size.
        _dwScreenBufferSize = _dwWindowSize;
    }
}

// WARNING: this function doesn't perform any validation or conversion
void Settings::InitFromStateInfo(_In_ PCONSOLE_STATE_INFO pStateInfo)
{
    _wFillAttribute = pStateInfo->ScreenAttributes;
    _wPopupFillAttribute = pStateInfo->PopupAttributes;
    _dwScreenBufferSize = pStateInfo->ScreenBufferSize;
    _dwWindowSize = pStateInfo->WindowSize;
    _dwWindowOrigin.X = (SHORT)pStateInfo->WindowPosX;
    _dwWindowOrigin.Y = (SHORT)pStateInfo->WindowPosY;
    _dwFontSize = pStateInfo->FontSize;
    _uFontFamily = pStateInfo->FontFamily;
    _uFontWeight = pStateInfo->FontWeight;
    StringCchCopyW(_FaceName, ARRAYSIZE(_FaceName), pStateInfo->FaceName);
    _uCursorSize = pStateInfo->CursorSize;
    _bFullScreen = pStateInfo->FullScreen;
    _bQuickEdit = pStateInfo->QuickEdit;
    _bAutoPosition = pStateInfo->AutoPosition;
    _bInsertMode = pStateInfo->InsertMode;
    _bHistoryNoDup = pStateInfo->HistoryNoDup;
    _uHistoryBufferSize = pStateInfo->HistoryBufferSize;
    _uNumberOfHistoryBuffers = pStateInfo->NumberOfHistoryBuffers;
    memmove(_ColorTable, pStateInfo->ColorTable, sizeof(_ColorTable));
    _uCodePage = pStateInfo->CodePage;
    _bWrapText = !!pStateInfo->fWrapText;
    _fFilterOnPaste = pStateInfo->fFilterOnPaste;
    _fCtrlKeyShortcutsDisabled = pStateInfo->fCtrlKeyShortcutsDisabled;
    _bLineSelection = pStateInfo->fLineSelection;
    _bWindowAlpha = pStateInfo->bWindowTransparency;
    _CursorColor = pStateInfo->CursorColor;
    _CursorType = static_cast<CursorType>(pStateInfo->CursorType);
    _fInterceptCopyPaste = pStateInfo->InterceptCopyPaste;
    _DefaultForeground = pStateInfo->DefaultForeground;
    _DefaultBackground = pStateInfo->DefaultBackground;
    _TerminalScrolling = pStateInfo->TerminalScrolling;
}

// Method Description:
// - Create a CONSOLE_STATE_INFO with the current state of this settings structure.
// Arguments:
// - <none>
// Return Value:
// - a CONSOLE_STATE_INFO with the current state of this settings structure.
CONSOLE_STATE_INFO Settings::CreateConsoleStateInfo() const
{
    CONSOLE_STATE_INFO csi = { 0 };
    csi.ScreenAttributes = _wFillAttribute;
    csi.PopupAttributes = _wPopupFillAttribute;
    csi.ScreenBufferSize = _dwScreenBufferSize;
    csi.WindowSize = _dwWindowSize;
    csi.WindowPosX = (SHORT)_dwWindowOrigin.X;
    csi.WindowPosY = (SHORT)_dwWindowOrigin.Y;
    csi.FontSize = _dwFontSize;
    csi.FontFamily = _uFontFamily;
    csi.FontWeight = _uFontWeight;
    StringCchCopyW(csi.FaceName, ARRAYSIZE(_FaceName), _FaceName);
    csi.CursorSize = _uCursorSize;
    csi.FullScreen = _bFullScreen;
    csi.QuickEdit = _bQuickEdit;
    csi.AutoPosition = _bAutoPosition;
    csi.InsertMode = _bInsertMode;
    csi.HistoryNoDup = _bHistoryNoDup;
    csi.HistoryBufferSize = _uHistoryBufferSize;
    csi.NumberOfHistoryBuffers = _uNumberOfHistoryBuffers;
    memmove(csi.ColorTable, _ColorTable, sizeof(_ColorTable));
    csi.CodePage = _uCodePage;
    csi.fWrapText = !!_bWrapText;
    csi.fFilterOnPaste = _fFilterOnPaste;
    csi.fCtrlKeyShortcutsDisabled = _fCtrlKeyShortcutsDisabled;
    csi.fLineSelection = _bLineSelection;
    csi.bWindowTransparency = _bWindowAlpha;
    csi.CursorColor = _CursorColor;
    csi.CursorType = static_cast<unsigned int>(_CursorType);
    csi.InterceptCopyPaste = _fInterceptCopyPaste;
    csi.DefaultForeground = _DefaultForeground;
    csi.DefaultBackground = _DefaultBackground;
    csi.TerminalScrolling = _TerminalScrolling;
    return csi;
}

void Settings::Validate()
{
    // If we were explicitly given a size in pixels from the startup info, divide by the font to turn it into characters.
    // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331%28v=vs.85%29.aspx
    if (WI_IsFlagSet(_dwStartupFlags, STARTF_USESIZE))
    {
        // TODO: FIX
        //// Get the font that we're going to use to convert pixels to characters.
        //DWORD const dwFontIndexWant = FindCreateFont(_uFontFamily,
        //                                             _FaceName,
        //                                             _dwFontSize,
        //                                             _uFontWeight,
        //                                             _uCodePage);

        //_dwWindowSize.X /= g_pfiFontInfo[dwFontIndexWant].Size.X;
        //_dwWindowSize.Y /= g_pfiFontInfo[dwFontIndexWant].Size.Y;
    }

    // minimum screen buffer size 1x1
    _dwScreenBufferSize.X = std::max(_dwScreenBufferSize.X, 1i16);
    _dwScreenBufferSize.Y = std::max(_dwScreenBufferSize.Y, 1i16);

    // minimum window size size 1x1
    _dwWindowSize.X = std::max(_dwWindowSize.X, 1i16);
    _dwWindowSize.Y = std::max(_dwWindowSize.Y, 1i16);

    // if buffer size is less than window size, increase buffer size to meet window size
    _dwScreenBufferSize.X = std::max(_dwWindowSize.X, _dwScreenBufferSize.X);
    _dwScreenBufferSize.Y = std::max(_dwWindowSize.Y, _dwScreenBufferSize.Y);

    // ensure that the window alpha value is not below the minimum. (no invisible windows)
    // if it's below minimum, just set it to the opaque value
    if (_bWindowAlpha < MIN_WINDOW_OPACITY)
    {
        _bWindowAlpha = BYTE_MAX;
    }

    // If text wrapping is on, ensure that the window width is the same as the buffer width.
    if (_bWrapText)
    {
        _dwWindowSize.X = _dwScreenBufferSize.X;
    }

    // Ensure that our fill attributes only contain colors and not any box drawing or invert attributes.
    WI_ClearAllFlags(_wFillAttribute, ~(FG_ATTRS | BG_ATTRS));
    WI_ClearAllFlags(_wPopupFillAttribute, ~(FG_ATTRS | BG_ATTRS));

    // If the extended color options are set to invalid values (all the same color), reset them.
    if (_CursorColor != Cursor::s_InvertCursorColor && _CursorColor == _DefaultBackground)
    {
        _CursorColor = Cursor::s_InvertCursorColor;
    }

    if (_DefaultForeground != INVALID_COLOR && _DefaultForeground == _DefaultBackground)
    {
        // INVALID_COLOR is used as an "unset" sentinel in future attribute functions.
        _DefaultForeground = _DefaultBackground = INVALID_COLOR;
        // If the damaged settings _further_ propagated to the default fill attribute, fix it.
        if (_wFillAttribute == 0)
        {
            // These attributes were taken from the Settings ctor and equal "gray on black"
            _wFillAttribute = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        }
    }

    FAIL_FAST_IF(!(_dwWindowSize.X > 0));
    FAIL_FAST_IF(!(_dwWindowSize.Y > 0));
    FAIL_FAST_IF(!(_dwScreenBufferSize.X > 0));
    FAIL_FAST_IF(!(_dwScreenBufferSize.Y > 0));
}

DWORD Settings::GetVirtTermLevel() const
{
    return _dwVirtTermLevel;
}
void Settings::SetVirtTermLevel(const DWORD dwVirtTermLevel)
{
    _dwVirtTermLevel = dwVirtTermLevel;
}

bool Settings::IsAltF4CloseAllowed() const
{
    return _fAllowAltF4Close;
}
void Settings::SetAltF4CloseAllowed(const bool fAllowAltF4Close)
{
    _fAllowAltF4Close = fAllowAltF4Close;
}

bool Settings::IsReturnOnNewlineAutomatic() const
{
    return _fAutoReturnOnNewline;
}
void Settings::SetAutomaticReturnOnNewline(const bool fAutoReturnOnNewline)
{
    _fAutoReturnOnNewline = fAutoReturnOnNewline;
}

bool Settings::IsGridRenderingAllowedWorldwide() const
{
    return _fRenderGridWorldwide;
}
void Settings::SetGridRenderingAllowedWorldwide(const bool fGridRenderingAllowed)
{
    // Only trigger a notification and update the status if something has changed.
    if (_fRenderGridWorldwide != fGridRenderingAllowed)
    {
        _fRenderGridWorldwide = fGridRenderingAllowed;

        if (ServiceLocator::LocateGlobals().pRender != nullptr)
        {
            ServiceLocator::LocateGlobals().pRender->TriggerRedrawAll();
        }
    }
}

bool Settings::GetFilterOnPaste() const
{
    return _fFilterOnPaste;
}
void Settings::SetFilterOnPaste(const bool fFilterOnPaste)
{
    _fFilterOnPaste = fFilterOnPaste;
}

const WCHAR* const Settings::GetLaunchFaceName() const
{
    return _LaunchFaceName;
}
void Settings::SetLaunchFaceName(_In_ PCWSTR const LaunchFaceName, const size_t cchLength)
{
    StringCchCopyW(_LaunchFaceName, cchLength, LaunchFaceName);
}

UINT Settings::GetCodePage() const
{
    return _uCodePage;
}
void Settings::SetCodePage(const UINT uCodePage)
{
    _uCodePage = uCodePage;
}

UINT Settings::GetScrollScale() const
{
    return _uScrollScale;
}
void Settings::SetScrollScale(const UINT uScrollScale)
{
    _uScrollScale = uScrollScale;
}

bool Settings::GetTrimLeadingZeros() const
{
    return _fTrimLeadingZeros;
}
void Settings::SetTrimLeadingZeros(const bool fTrimLeadingZeros)
{
    _fTrimLeadingZeros = fTrimLeadingZeros;
}

bool Settings::GetEnableColorSelection() const
{
    return _fEnableColorSelection;
}
void Settings::SetEnableColorSelection(const bool fEnableColorSelection)
{
    _fEnableColorSelection = fEnableColorSelection;
}

bool Settings::GetLineSelection() const
{
    return _bLineSelection;
}
void Settings::SetLineSelection(const bool bLineSelection)
{
    _bLineSelection = bLineSelection;
}

bool Settings::GetWrapText() const
{
    return _bWrapText;
}
void Settings::SetWrapText(const bool bWrapText)
{
    _bWrapText = bWrapText;
}

bool Settings::GetCtrlKeyShortcutsDisabled() const
{
    return _fCtrlKeyShortcutsDisabled;
}
void Settings::SetCtrlKeyShortcutsDisabled(const bool fCtrlKeyShortcutsDisabled)
{
    _fCtrlKeyShortcutsDisabled = fCtrlKeyShortcutsDisabled;
}

BYTE Settings::GetWindowAlpha() const
{
    return _bWindowAlpha;
}
void Settings::SetWindowAlpha(const BYTE bWindowAlpha)
{
    // if we're out of bounds, set it to 100% opacity so it appears as if nothing happened.
    _bWindowAlpha = (bWindowAlpha < MIN_WINDOW_OPACITY) ? BYTE_MAX : bWindowAlpha;
}

DWORD Settings::GetHotKey() const
{
    return _dwHotKey;
}
void Settings::SetHotKey(const DWORD dwHotKey)
{
    _dwHotKey = dwHotKey;
}

DWORD Settings::GetStartupFlags() const
{
    return _dwStartupFlags;
}
void Settings::SetStartupFlags(const DWORD dwStartupFlags)
{
    _dwStartupFlags = dwStartupFlags;
}

WORD Settings::GetFillAttribute() const
{
    return _wFillAttribute;
}
void Settings::SetFillAttribute(const WORD wFillAttribute)
{
    _wFillAttribute = wFillAttribute;

    // Do not allow the default fill attribute to use any attrs other than fg/bg colors.
    // This prevents us from accidentally inverting everything or suddenly drawing lines
    // everywhere by default.
    WI_ClearAllFlags(_wFillAttribute, ~(FG_ATTRS | BG_ATTRS));
}

WORD Settings::GetPopupFillAttribute() const
{
    return _wPopupFillAttribute;
}
void Settings::SetPopupFillAttribute(const WORD wPopupFillAttribute)
{
    _wPopupFillAttribute = wPopupFillAttribute;

    // Do not allow the default popup fill attribute to use any attrs other than fg/bg colors.
    // This prevents us from accidentally inverting everything or suddenly drawing lines
    // everywhere by defualt.
    WI_ClearAllFlags(_wPopupFillAttribute, ~(FG_ATTRS | BG_ATTRS));
}

WORD Settings::GetShowWindow() const
{
    return _wShowWindow;
}
void Settings::SetShowWindow(const WORD wShowWindow)
{
    _wShowWindow = wShowWindow;
}

WORD Settings::GetReserved() const
{
    return _wReserved;
}
void Settings::SetReserved(const WORD wReserved)
{
    _wReserved = wReserved;
}

COORD Settings::GetScreenBufferSize() const
{
    return _dwScreenBufferSize;
}
void Settings::SetScreenBufferSize(const COORD dwScreenBufferSize)
{
    _dwScreenBufferSize = dwScreenBufferSize;
}

COORD Settings::GetWindowSize() const
{
    return _dwWindowSize;
}
void Settings::SetWindowSize(const COORD dwWindowSize)
{
    _dwWindowSize = dwWindowSize;
}

bool Settings::IsWindowSizePixelsValid() const
{
    return _fUseWindowSizePixels;
}
COORD Settings::GetWindowSizePixels() const
{
    return _dwWindowSizePixels;
}
void Settings::SetWindowSizePixels(const COORD dwWindowSizePixels)
{
    _dwWindowSizePixels = dwWindowSizePixels;
}

COORD Settings::GetWindowOrigin() const
{
    return _dwWindowOrigin;
}
void Settings::SetWindowOrigin(const COORD dwWindowOrigin)
{
    _dwWindowOrigin = dwWindowOrigin;
}

DWORD Settings::GetFont() const
{
    return _nFont;
}
void Settings::SetFont(const DWORD nFont)
{
    _nFont = nFont;
}

COORD Settings::GetFontSize() const
{
    return _dwFontSize;
}
void Settings::SetFontSize(const COORD dwFontSize)
{
    _dwFontSize = dwFontSize;
}

UINT Settings::GetFontFamily() const
{
    return _uFontFamily;
}
void Settings::SetFontFamily(const UINT uFontFamily)
{
    _uFontFamily = uFontFamily;
}

UINT Settings::GetFontWeight() const
{
    return _uFontWeight;
}
void Settings::SetFontWeight(const UINT uFontWeight)
{
    _uFontWeight = uFontWeight;
}

const WCHAR* const Settings::GetFaceName() const
{
    return _FaceName;
}
void Settings::SetFaceName(_In_ PCWSTR const pcszFaceName, const size_t cchLength)
{
    StringCchCopyW(_FaceName, cchLength, pcszFaceName);
}

UINT Settings::GetCursorSize() const
{
    return _uCursorSize;
}
void Settings::SetCursorSize(const UINT uCursorSize)
{
    _uCursorSize = uCursorSize;
}

bool Settings::GetFullScreen() const
{
    return _bFullScreen;
}
void Settings::SetFullScreen(const bool bFullScreen)
{
    _bFullScreen = bFullScreen;
}

bool Settings::GetQuickEdit() const
{
    return _bQuickEdit;
}
void Settings::SetQuickEdit(const bool bQuickEdit)
{
    _bQuickEdit = bQuickEdit;
}

bool Settings::GetInsertMode() const
{
    return _bInsertMode;
}
void Settings::SetInsertMode(const bool bInsertMode)
{
    _bInsertMode = bInsertMode;
}

bool Settings::GetAutoPosition() const
{
    return _bAutoPosition;
}
void Settings::SetAutoPosition(const bool bAutoPosition)
{
    _bAutoPosition = bAutoPosition;
}

UINT Settings::GetHistoryBufferSize() const
{
    return _uHistoryBufferSize;
}
void Settings::SetHistoryBufferSize(const UINT uHistoryBufferSize)
{
    _uHistoryBufferSize = uHistoryBufferSize;
}

UINT Settings::GetNumberOfHistoryBuffers() const
{
    return _uNumberOfHistoryBuffers;
}
void Settings::SetNumberOfHistoryBuffers(const UINT uNumberOfHistoryBuffers)
{
    _uNumberOfHistoryBuffers = uNumberOfHistoryBuffers;
}

bool Settings::GetHistoryNoDup() const
{
    return _bHistoryNoDup;
}
void Settings::SetHistoryNoDup(const bool bHistoryNoDup)
{
    _bHistoryNoDup = bHistoryNoDup;
}

const COLORREF* const Settings::GetColorTable() const
{
    return _ColorTable;
}
void Settings::SetColorTable(_In_reads_(cSize) const COLORREF* const pColorTable, const size_t cSize)
{
    size_t cSizeWritten = std::min(cSize, static_cast<size_t>(COLOR_TABLE_SIZE));

    memmove(_ColorTable, pColorTable, cSizeWritten * sizeof(COLORREF));
}
void Settings::SetColorTableEntry(const size_t index, const COLORREF ColorValue)
{
    if (index < ARRAYSIZE(_ColorTable))
    {
        _ColorTable[index] = ColorValue;
    }
    else
    {
        _XtermColorTable[index] = ColorValue;
    }
}

bool Settings::IsStartupTitleIsLinkNameSet() const
{
    return WI_IsFlagSet(_dwStartupFlags, STARTF_TITLEISLINKNAME);
}

bool Settings::IsFaceNameSet() const
{
    return GetFaceName()[0] != '\0';
}

void Settings::UnsetStartupFlag(const DWORD dwFlagToUnset)
{
    _dwStartupFlags &= ~dwFlagToUnset;
}

const size_t Settings::GetColorTableSize() const
{
    return ARRAYSIZE(_ColorTable);
}

COLORREF Settings::GetColorTableEntry(const size_t index) const
{
    if (index < ARRAYSIZE(_ColorTable))
    {
        return _ColorTable[index];
    }
    else
    {
        return _XtermColorTable[index];
    }
}

// Routine Description:
// - Generates a legacy attribute from the given TextAttributes.
//     This needs to be a method on the Settings because the generated index
//     is dependent upon the particular values of the color table at the time of reading.
// Parameters:
// - attributes - The TextAttributes to generate a legacy attribute for.
// Return value:
// - A WORD representing the entry in the color table that most closely represents the given fullcolor attributes.
WORD Settings::GenerateLegacyAttributes(const TextAttribute attributes) const
{
    const WORD wLegacyOriginal = attributes.GetLegacyAttributes();
    if (attributes.IsLegacy())
    {
        return wLegacyOriginal;
    }
    // We need to construct the legacy attributes manually
    // First start with whatever our default legacy attributes are
    BYTE fgIndex = static_cast<BYTE>((_wFillAttribute & FG_ATTRS));
    BYTE bgIndex = static_cast<BYTE>((_wFillAttribute & BG_ATTRS) >> 4);
    // If the attributes have any RGB components, we need to match that RGB
    //      color to a color table value.
    if (attributes.IsRgb())
    {
        // If the attribute doesn't have a "default" colored *ground, look up
        //  the nearest color table value for its *ground.
        const COLORREF rgbForeground = LookupForegroundColor(attributes);
        fgIndex = attributes.ForegroundIsDefault() ?
                      fgIndex :
                      static_cast<BYTE>(FindNearestTableIndex(rgbForeground));

        const COLORREF rgbBackground = LookupBackgroundColor(attributes);
        bgIndex = attributes.BackgroundIsDefault() ?
                      bgIndex :
                      static_cast<BYTE>(FindNearestTableIndex(rgbBackground));
    }

    // TextAttribute::GetLegacyAttributes(BYTE, BYTE) will use the legacy value
    //      it has if the component is a legacy index, otherwise it will use the
    //      provided byte for each index. In this way, we can provide a value to
    //      use should it not already have one.
    const WORD wCompleteAttr = attributes.GetLegacyAttributes(fgIndex, bgIndex);
    return wCompleteAttr;
}

//Routine Description:
// For a given RGB color Color, finds the nearest color from the array ColorTable, and returns the index of that match.
//Arguments:
// - Color - The RGB color to fine the nearest color to.
// - ColorTable - The array of colors to find a nearest color from.
// - cColorTable - The number of elements in ColorTable
// Return value:
// The index in ColorTable of the nearest match to Color.
WORD Settings::FindNearestTableIndex(const COLORREF Color) const
{
    return ::FindNearestTableIndex(Color, _ColorTable, ARRAYSIZE(_ColorTable));
}

COLORREF Settings::GetCursorColor() const noexcept
{
    return _CursorColor;
}

CursorType Settings::GetCursorType() const noexcept
{
    return _CursorType;
}

void Settings::SetCursorColor(const COLORREF CursorColor) noexcept
{
    _CursorColor = CursorColor;
}

void Settings::SetCursorType(const CursorType cursorType) noexcept
{
    _CursorType = cursorType;
}

bool Settings::GetInterceptCopyPaste() const noexcept
{
    return _fInterceptCopyPaste;
}

void Settings::SetInterceptCopyPaste(const bool interceptCopyPaste) noexcept
{
    _fInterceptCopyPaste = interceptCopyPaste;
}

COLORREF Settings::GetDefaultForegroundColor() const noexcept
{
    return _DefaultForeground;
}

void Settings::SetDefaultForegroundColor(const COLORREF defaultForeground) noexcept
{
    _DefaultForeground = defaultForeground;
}

COLORREF Settings::GetDefaultBackgroundColor() const noexcept
{
    return _DefaultBackground;
}

void Settings::SetDefaultBackgroundColor(const COLORREF defaultBackground) noexcept
{
    _DefaultBackground = defaultBackground;
}

TextAttribute Settings::GetDefaultAttributes() const noexcept
{
    auto attrs = TextAttribute{ _wFillAttribute };
    if (_DefaultForeground != INVALID_COLOR)
    {
        attrs.SetDefaultForeground();
    }
    if (_DefaultBackground != INVALID_COLOR)
    {
        attrs.SetDefaultBackground();
    }
    return attrs;
}

bool Settings::IsTerminalScrolling() const noexcept
{
    return _TerminalScrolling;
}

void Settings::SetTerminalScrolling(const bool terminalScrollingEnabled) noexcept
{
    _TerminalScrolling = terminalScrollingEnabled;
}

// Routine Description:
// - Determines whether our primary renderer should be DirectX or GDI.
// - This is based on user preference and velocity hold back state.
// Return Value:
// - True means use DirectX renderer. False means use GDI renderer.
bool Settings::GetUseDx() const noexcept
{
    return _fUseDx;
}

// Method Description:
// - Return the default foreground color of the console. If the settings are
//      configured to have a default foreground color (separate from the color
//      table), this will return that value. Otherwise it will return the value
//      from the colortable corresponding to our default legacy attributes.
// Arguments:
// - <none>
// Return Value:
// - the default foreground color of the console.
COLORREF Settings::CalculateDefaultForeground() const noexcept
{
    const auto fg = GetDefaultForegroundColor();
    return fg != INVALID_COLOR ? fg : ForegroundColor(GetFillAttribute(), GetColorTable(), GetColorTableSize());
}

// Method Description:
// - Return the default background color of the console. If the settings are
//      configured to have a default background color (separate from the color
//      table), this will return that value. Otherwise it will return the value
//      from the colortable corresponding to our default legacy attributes.
// Arguments:
// - <none>
// Return Value:
// - the default background color of the console.
COLORREF Settings::CalculateDefaultBackground() const noexcept
{
    const auto bg = GetDefaultBackgroundColor();
    return bg != INVALID_COLOR ? bg : BackgroundColor(GetFillAttribute(), GetColorTable(), GetColorTableSize());
}

// Method Description:
// - Get the foregroud color of a particular text attribute, using our color
//      table, and our configured default attributes.
// Arguments:
// - attr: the TextAttribute to retrieve the foreground color of.
// Return Value:
// - The color value of the attribute's foreground TextColor.
COLORREF Settings::LookupForegroundColor(const TextAttribute& attr) const noexcept
{
    const auto tableView = std::basic_string_view<COLORREF>(&GetColorTable()[0], GetColorTableSize());
    return attr.CalculateRgbForeground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
}

// Method Description:
// - Get the background color of a particular text attribute, using our color
//      table, and our configured default attributes.
// Arguments:
// - attr: the TextAttribute to retrieve the background color of.
// Return Value:
// - The color value of the attribute's background TextColor.
COLORREF Settings::LookupBackgroundColor(const TextAttribute& attr) const noexcept
{
    const auto tableView = std::basic_string_view<COLORREF>(&GetColorTable()[0], GetColorTableSize());
    return attr.CalculateRgbBackground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
}

bool Settings::GetCopyColor() const noexcept
{
    return _fCopyColor;
}
