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

#include "pch.h"
#include "ScopedResourceLoader.h"
#include "LibraryResources.h"

/*
CHECKED RESOURCES
This is the support infrastructure for "checked resources", a system that lets
us immediately failfast (on launch) when a library makes a static reference to
a resource that doesn't exist at runtime.

Resource checking relies on diligent use of USES_RESOURCE() and RS_() (which
uses USES_RESOURCE), but can make sure we don't ship something that'll blow up
at runtime.

It works like this:
** IN DEBUG MODE **
- All resource names referenced through USES_RESOURCE() are emitted alongside
  their referencing filenames and line numbers into a static section of the
  binary.
  That section is named .util$res$m.

- We emit two sentinel values into two different sections, .util$res$a and
  .util$res$z.

- The linker sorts all sections alphabetically before crushing them together
  into the final binary.

- When we first construct our library's scoped resource loader, we iterate over
  every resource reference between $a and $z and check residency.

** IN RELEASE MODE **
- All checked resource code is compiled out.
*/

extern const wchar_t* g_WinRTUtilsLibraryResourceScope;

#ifdef _DEBUG
#pragma detect_mismatch("winrt_utils_debug", "1")

#pragma section(".util$res$a", read)
#pragma section(".util$res$z", read)

__declspec(allocate(".util$res$a")) static const ::Microsoft::Console::Utils::StaticResource* debugResFirst{ nullptr };
__declspec(allocate(".util$res$z")) static const ::Microsoft::Console::Utils::StaticResource* debugResLast{ nullptr };

static void EnsureAllResourcesArePresent(const ScopedResourceLoader& loader)
{
    for (auto resp = &debugResFirst; resp != &debugResLast; ++resp)
    {
        if (*resp)
        {
            const auto& res = **resp;
            if (!loader.HasResourceWithName(res.resourceKey))
            {
                auto filename = wcsrchr(res.filename, L'\\');
                if (!filename)
                {
                    filename = res.filename;
                }
                else
                {
                    filename++; // skip the '\'
                }

                FAIL_FAST_MSG("Resource %ls not found in scope %ls (%ls:%u)", res.resourceKey, g_WinRTUtilsLibraryResourceScope, filename, res.line);
            }
        }
    }
}
#else // _DEBUG

#pragma detect_mismatch("winrt_utils_debug", "0")

#endif

static ScopedResourceLoader GetLibraryResourceLoader()
try
{
    ScopedResourceLoader loader{ g_WinRTUtilsLibraryResourceScope };
#ifdef _DEBUG
    EnsureAllResourcesArePresent(loader);
#endif
    return loader;
}
CATCH_FAIL_FAST()

winrt::hstring GetLibraryResourceString(const std::wstring_view key)
try
{
    static auto loader{ GetLibraryResourceLoader() };
    return loader.GetLocalizedString(key);
}
CATCH_FAIL_FAST()

bool HasLibraryResourceWithName(const std::wstring_view key)
try
{
    static auto loader{ GetLibraryResourceLoader() };
    return loader.HasResourceWithName(key);
}
CATCH_FAIL_FAST()
