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

Module Name:
- ConsoleArguments.hpp

Abstract:
- Encapsulates the commandline arguments to the console host.

Author(s):
- Mike Griese (migrie) 07-Sept-2017
--*/

#pragma once

#ifdef UNIT_TESTING
#include "WexTestClass.h"
#endif

class ConsoleArguments
{
public:
    ConsoleArguments(const std::wstring& commandline,
                     const HANDLE hStdIn,
                     const HANDLE hStdOut);

    ConsoleArguments();

    ConsoleArguments& operator=(const ConsoleArguments& other);

    [[nodiscard]] HRESULT ParseCommandline();

    bool HasVtHandles() const;
    bool InConptyMode() const noexcept;
    bool IsHeadless() const;
    bool ShouldCreateServerHandle() const;

    HANDLE GetServerHandle() const;
    HANDLE GetVtInHandle() const;
    HANDLE GetVtOutHandle() const;

    bool HasSignalHandle() const;
    HANDLE GetSignalHandle() const;

    std::wstring GetClientCommandline() const;
    std::wstring GetVtMode() const;
    bool GetForceV1() const;

    short GetWidth() const;
    short GetHeight() const;
    bool GetInheritCursor() const;

    void SetExpectedSize(COORD dimensions) noexcept;

    static const std::wstring_view VT_MODE_ARG;
    static const std::wstring_view HEADLESS_ARG;
    static const std::wstring_view SERVER_HANDLE_ARG;
    static const std::wstring_view SIGNAL_HANDLE_ARG;
    static const std::wstring_view HANDLE_PREFIX;
    static const std::wstring_view CLIENT_COMMANDLINE_ARG;
    static const std::wstring_view FORCE_V1_ARG;
    static const std::wstring_view FILEPATH_LEADER_PREFIX;
    static const std::wstring_view WIDTH_ARG;
    static const std::wstring_view HEIGHT_ARG;
    static const std::wstring_view INHERIT_CURSOR_ARG;
    static const std::wstring_view FEATURE_ARG;
    static const std::wstring_view FEATURE_PTY_ARG;

private:
#ifdef UNIT_TESTING
    // This accessor used to create a copy of this class for unit testing comparison ease.
    ConsoleArguments(const std::wstring commandline,
                     const std::wstring clientCommandline,
                     const HANDLE vtInHandle,
                     const HANDLE vtOutHandle,
                     const std::wstring vtMode,
                     const short width,
                     const short height,
                     const bool forceV1,
                     const bool headless,
                     const bool createServerHandle,
                     const DWORD serverHandle,
                     const DWORD signalHandle,
                     const bool inheritCursor) :
        _commandline(commandline),
        _clientCommandline(clientCommandline),
        _vtInHandle(vtInHandle),
        _vtOutHandle(vtOutHandle),
        _vtMode(vtMode),
        _width(width),
        _height(height),
        _forceV1(forceV1),
        _headless(headless),
        _createServerHandle(createServerHandle),
        _serverHandle(serverHandle),
        _signalHandle(signalHandle),
        _inheritCursor(inheritCursor),
        _recievedEarlySizeChange{ false },
        _originalWidth{ -1 },
        _originalHeight{ -1 }
    {
    }
#endif

    std::wstring _commandline;

    std::wstring _clientCommandline;

    HANDLE _vtInHandle;

    HANDLE _vtOutHandle;

    std::wstring _vtMode;

    bool _forceV1;
    bool _headless;

    short _width;
    short _height;

    bool _createServerHandle;
    DWORD _serverHandle;
    DWORD _signalHandle;
    bool _inheritCursor;

    bool _recievedEarlySizeChange;
    short _originalWidth;
    short _originalHeight;

    [[nodiscard]] HRESULT _GetClientCommandline(_Inout_ std::vector<std::wstring>& args,
                                                const size_t index,
                                                const bool skipFirst);

    static void s_ConsumeArg(_Inout_ std::vector<std::wstring>& args,
                             _In_ size_t& index);
    [[nodiscard]] static HRESULT s_GetArgumentValue(_Inout_ std::vector<std::wstring>& args,
                                                    _Inout_ size_t& index,
                                                    _Out_opt_ std::wstring* const pSetting);
    [[nodiscard]] static HRESULT s_GetArgumentValue(_Inout_ std::vector<std::wstring>& args,
                                                    _Inout_ size_t& index,
                                                    _Out_opt_ short* const pSetting);
    [[nodiscard]] static HRESULT s_HandleFeatureValue(_Inout_ std::vector<std::wstring>& args,
                                                      _Inout_ size_t& index);

    [[nodiscard]] static HRESULT s_ParseHandleArg(const std::wstring& handleAsText,
                                                  _Inout_ DWORD& handleAsVal);

#ifdef UNIT_TESTING
    friend class ConsoleArgumentsTests;
#endif
};

#ifdef UNIT_TESTING
namespace WEX
{
    namespace TestExecution
    {
        template<>
        class VerifyOutputTraits<ConsoleArguments>
        {
        public:
            static WEX::Common::NoThrowString ToString(const ConsoleArguments& ci)
            {
                return WEX::Common::NoThrowString().Format(L"\r\nClient Command Line: '%ws',\r\n"
                                                           L"Use VT Handles: '%ws',\r\n"
                                                           L"VT In Handle: '0x%x',\r\n"
                                                           L"VT Out Handle: '0x%x',\r\n"
                                                           L"Vt Mode: '%ws',\r\n"
                                                           L"WidthxHeight: '%dx%d',\r\n"
                                                           L"ForceV1: '%ws',\r\n"
                                                           L"Headless: '%ws',\r\n"
                                                           L"Create Server Handle: '%ws',\r\n"
                                                           L"Server Handle: '0x%x'\r\n"
                                                           L"Use Signal Handle: '%ws'\r\n"
                                                           L"Signal Handle: '0x%x'\r\n",
                                                           L"Inherit Cursor: '%ws'\r\n",
                                                           ci.GetClientCommandline().c_str(),
                                                           s_ToBoolString(ci.HasVtHandles()),
                                                           ci.GetVtInHandle(),
                                                           ci.GetVtOutHandle(),
                                                           ci.GetVtMode().c_str(),
                                                           ci.GetWidth(),
                                                           ci.GetHeight(),
                                                           s_ToBoolString(ci.GetForceV1()),
                                                           s_ToBoolString(ci.IsHeadless()),
                                                           s_ToBoolString(ci.ShouldCreateServerHandle()),
                                                           ci.GetServerHandle(),
                                                           s_ToBoolString(ci.HasSignalHandle()),
                                                           ci.GetSignalHandle(),
                                                           s_ToBoolString(ci.GetInheritCursor()));
            }

        private:
            static PCWSTR s_ToBoolString(const bool val)
            {
                return val ? L"true" : L"false";
            }
        };

        template<>
        class VerifyCompareTraits<ConsoleArguments, ConsoleArguments>
        {
        public:
            static bool AreEqual(const ConsoleArguments& expected, const ConsoleArguments& actual)
            {
                return expected.GetClientCommandline() == actual.GetClientCommandline() &&
                       expected.HasVtHandles() == actual.HasVtHandles() &&
                       expected.GetVtInHandle() == actual.GetVtInHandle() &&
                       expected.GetVtOutHandle() == actual.GetVtOutHandle() &&
                       expected.GetVtMode() == actual.GetVtMode() &&
                       expected.GetWidth() == actual.GetWidth() &&
                       expected.GetHeight() == actual.GetHeight() &&
                       expected.GetForceV1() == actual.GetForceV1() &&
                       expected.IsHeadless() == actual.IsHeadless() &&
                       expected.ShouldCreateServerHandle() == actual.ShouldCreateServerHandle() &&
                       expected.GetServerHandle() == actual.GetServerHandle() &&
                       expected.HasSignalHandle() == actual.HasSignalHandle() &&
                       expected.GetSignalHandle() == actual.GetSignalHandle() &&
                       expected.GetInheritCursor() == actual.GetInheritCursor();
            }

            static bool AreSame(const ConsoleArguments& expected, const ConsoleArguments& actual)
            {
                return &expected == &actual;
            }

            static bool IsLessThan(const ConsoleArguments&, const ConsoleArguments&) = delete;

            static bool IsGreaterThan(const ConsoleArguments&, const ConsoleArguments&) = delete;

            static bool IsNull(const ConsoleArguments& object)
            {
                return object.GetClientCommandline().empty() &&
                       (object.GetVtInHandle() == 0 || object.GetVtInHandle() == INVALID_HANDLE_VALUE) &&
                       (object.GetVtOutHandle() == 0 || object.GetVtOutHandle() == INVALID_HANDLE_VALUE) &&
                       object.GetVtMode().empty() &&
                       !object.GetForceV1() &&
                       (object.GetWidth() == 0) &&
                       (object.GetHeight() == 0) &&
                       !object.IsHeadless() &&
                       !object.ShouldCreateServerHandle() &&
                       object.GetServerHandle() == 0 &&
                       (object.GetSignalHandle() == 0 || object.GetSignalHandle() == INVALID_HANDLE_VALUE) &&
                       !object.GetInheritCursor();
            }
        };
    }
}
#endif
