#include "pch.h"

#include <msclr/marshal.h>
#include <msclr/marshal_cppstd.h>
#include <functional>
#include "keyboard_layout.h"
#include "two_way_pipe_message_ipc.h"
#include "shared_constants.h"

// We cannot use C++/WinRT APIs when compiled with /clr (we'll get a runtime crash). os-detect API is used
// in both native C++ and C++/CX.
// We also cannot compile it as a library, since we use different cppruntime linkage in C++/CX and native C++.
// Therefore the simplest way is to compile these functions as native using the pragmas below.
#pragma managed(push, off)
#include "../utils/os-detect.h"
#pragma managed(pop)

#include <common/version/version.h>

using namespace System;
using namespace System::Runtime::InteropServices;

// https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
namespace interop
{
public
    ref class LayoutMapManaged
    {
    public:
        LayoutMapManaged() :
            _map(new LayoutMap) {}

        ~LayoutMapManaged()
        {
            delete _map;
        }

        String ^ GetKeyName(DWORD key) {
            return gcnew String(_map->GetKeyName(key).c_str());
        }

            void Updatelayout()
        {
            _map->UpdateLayout();
        }

    protected:
        !LayoutMapManaged()
        {
            delete _map;
        }

    private:
        LayoutMap* _map;
    };

public
    ref class TwoWayPipeMessageIPCManaged
    {
    public:
        delegate void ReadCallback(String ^ message);

        TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
        {
            _wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
            _callback = callback;

            TwoWayPipeMessageIPC::callback_function cb = nullptr;
            if (callback != nullptr)
            {
                cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
            }
            _pipe = new TwoWayPipeMessageIPC(
                msclr::interop::marshal_as<std::wstring>(inputPipeName),
                msclr::interop::marshal_as<std::wstring>(outputPipeName),
                cb);
        }

        ~TwoWayPipeMessageIPCManaged()
        {
            delete _pipe;
        }

        void Send(String ^ msg)
        {
            _pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
        }

        void Start()
        {
            _pipe->start(nullptr);
        }

        void End()
        {
            _pipe->end();
        }

    protected:
        !TwoWayPipeMessageIPCManaged()
        {
            delete _pipe;
        }

    private:
        delegate void InternalReadCallback(const std::wstring& msg);

        TwoWayPipeMessageIPC* _pipe;
        ReadCallback ^ _callback;
        InternalReadCallback ^ _wrapperCallback;

        void ReadCallbackHelper(const std::wstring& msg)
        {
            _callback(gcnew String(msg.c_str()));
        }
    };

public
    ref class CommonManaged
    {
    public:
        static String ^ GetProductVersion() {
            return gcnew String(get_product_version().c_str());
        }

            static bool ShouldNewSettingsBeUsed()
        {
            return UseNewSettings();
        }
    };

public
    ref class Constants
    {
    public:
        literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;

        static String ^ PowerLauncherSharedEvent() {
            return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
        }
    };
}
