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

#include "precomp.h"
#include "WexTestClass.h"
#include "../../inc/consoletaeftemplates.hpp"

#include "misc.h"
#include "dbcs.h"
#include "../../types/inc/IInputEvent.hpp"

#include "../interactivity/inc/ServiceLocator.hpp"

#include <deque>
#include <memory>

using namespace WEX::Logging;
using Microsoft::Console::Interactivity::ServiceLocator;

class InputRecordConversionTests
{
    TEST_CLASS(InputRecordConversionTests);

    static const size_t INPUT_RECORD_COUNT = 10;
    UINT savedCodepage = 0;

    TEST_CLASS_SETUP(ClassSetup)
    {
        CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
        savedCodepage = gci.CP;
        gci.CP = CP_JAPANESE;
        VERIFY_IS_TRUE(!!GetCPInfo(gci.CP, &gci.CPInfo));
        return true;
    }

    TEST_CLASS_CLEANUP(ClassCleanup)
    {
        CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
        gci.CP = savedCodepage;
        VERIFY_IS_TRUE(!!GetCPInfo(gci.CP, &gci.CPInfo));
        return true;
    }

    TEST_METHOD(SplitToOemLeavesNonKeyEventsAlone)
    {
        Log::Comment(L"nothing should happen to input events that aren't key events");

        std::deque<std::unique_ptr<IInputEvent>> inEvents;
        INPUT_RECORD inRecords[INPUT_RECORD_COUNT] = { 0 };
        for (size_t i = 0; i < INPUT_RECORD_COUNT; ++i)
        {
            inRecords[i].EventType = MOUSE_EVENT;
            inRecords[i].Event.MouseEvent.dwMousePosition.X = static_cast<SHORT>(i);
            inRecords[i].Event.MouseEvent.dwMousePosition.Y = static_cast<SHORT>(i * 2);
            inEvents.push_back(IInputEvent::Create(inRecords[i]));
        }

        SplitToOem(inEvents);
        VERIFY_ARE_EQUAL(INPUT_RECORD_COUNT, inEvents.size());

        for (size_t i = 0; i < INPUT_RECORD_COUNT; ++i)
        {
            VERIFY_ARE_EQUAL(inRecords[i], inEvents[i]->ToInputRecord());
        }
    }

    TEST_METHOD(SplitToOemLeavesNonDbcsCharsAlone)
    {
        Log::Comment(L"non-dbcs chars shouldn't be split");

        std::deque<std::unique_ptr<IInputEvent>> inEvents;
        INPUT_RECORD inRecords[INPUT_RECORD_COUNT] = { 0 };
        for (size_t i = 0; i < INPUT_RECORD_COUNT; ++i)
        {
            inRecords[i].EventType = KEY_EVENT;
            inRecords[i].Event.KeyEvent.uChar.UnicodeChar = static_cast<wchar_t>(L'a' + i);
            inEvents.push_back(IInputEvent::Create(inRecords[i]));
        }

        SplitToOem(inEvents);
        VERIFY_ARE_EQUAL(INPUT_RECORD_COUNT, inEvents.size());

        for (size_t i = 0; i < INPUT_RECORD_COUNT; ++i)
        {
            VERIFY_ARE_EQUAL(inRecords[i], inEvents[i]->ToInputRecord());
        }
    }

    TEST_METHOD(SplitToOemSplitsDbcsChars)
    {
        Log::Comment(L"dbcs chars should be split");

        const UINT codepage = ServiceLocator::LocateGlobals().getConsoleInformation().CP;

        INPUT_RECORD inRecords[INPUT_RECORD_COUNT * 2] = { 0 };
        std::deque<std::unique_ptr<IInputEvent>> inEvents;
        // U+3042 hiragana letter A
        wchar_t hiraganaA = 0x3042;
        wchar_t inChars[INPUT_RECORD_COUNT];
        for (size_t i = 0; i < INPUT_RECORD_COUNT; ++i)
        {
            wchar_t currentChar = static_cast<wchar_t>(hiraganaA + (i * 2));
            inRecords[i].EventType = KEY_EVENT;
            inRecords[i].Event.KeyEvent.uChar.UnicodeChar = currentChar;
            inChars[i] = currentChar;
            inEvents.push_back(IInputEvent::Create(inRecords[i]));
        }

        SplitToOem(inEvents);
        VERIFY_ARE_EQUAL(INPUT_RECORD_COUNT * 2, inEvents.size());

        // create the data to compare the output to
        char dbcsChars[INPUT_RECORD_COUNT * 2] = { 0 };
        int writtenBytes = WideCharToMultiByte(codepage,
                                               0,
                                               inChars,
                                               INPUT_RECORD_COUNT,
                                               dbcsChars,
                                               INPUT_RECORD_COUNT * 2,
                                               nullptr,
                                               false);
        VERIFY_ARE_EQUAL(writtenBytes, static_cast<int>(INPUT_RECORD_COUNT * 2));
        for (size_t i = 0; i < INPUT_RECORD_COUNT * 2; ++i)
        {
            const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(inEvents[i].get());
            VERIFY_ARE_EQUAL(static_cast<char>(pKeyEvent->GetCharData()), dbcsChars[i]);
        }
    }
};
