// MobileGL - MobileGL/MG_State/EGLState/Core.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
//   https://www.gnu.org/licenses/gpl-3.0.txt
//   https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header

#include "Core.h"
#include <EGL/eglext.h>

namespace MobileGL {
    namespace MG_State {
        namespace EGLState {
            namespace {
                constexpr EGLint EGL_DISPLAY_MAJOR_VERSION = 1;
                constexpr EGLint EGL_DISPLAY_MINOR_VERSION = 5;
                constexpr EGLint DEFAULT_MAX_PBUFFER_SIZE = 16384;

                template <typename AttrType>
                Optional<AttrType> ParseAttribValue(const AttrType* attribList, EGLint attrib) {
                    if (!attribList) {
                        return Nullopt;
                    }
                    for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) {
                        if (attribList[i] == static_cast<AttrType>(attrib)) {
                            return attribList[i + 1];
                        }
                    }
                    return Nullopt;
                }

                EGLint QueryDefaultX11VisualId() {
#if defined(__linux__) && !defined(__ANDROID__)
                    const char* displayName = std::getenv("DISPLAY");
                    if (!displayName) {
                        return 0;
                    }

                    void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
                    if (!x11Lib) {
                        x11Lib = dlopen("libX11.so", RTLD_LOCAL | RTLD_NOW);
                    }
                    if (!x11Lib) {
                        return 0;
                    }

                    using XOpenDisplayFn = void* (*)(const char*);
                    using XDefaultScreenFn = int (*)(void*);
                    using XDefaultVisualFn = void* (*)(void*, int);
                    using XVisualIDFromVisualFn = unsigned long (*)(void*);
                    using XCloseDisplayFn = int (*)(void*);

                    auto* xOpenDisplay = reinterpret_cast<XOpenDisplayFn>(dlsym(x11Lib, "XOpenDisplay"));
                    auto* xDefaultScreen = reinterpret_cast<XDefaultScreenFn>(dlsym(x11Lib, "XDefaultScreen"));
                    auto* xDefaultVisual = reinterpret_cast<XDefaultVisualFn>(dlsym(x11Lib, "XDefaultVisual"));
                    auto* xVisualIDFromVisual =
                        reinterpret_cast<XVisualIDFromVisualFn>(dlsym(x11Lib, "XVisualIDFromVisual"));
                    auto* xCloseDisplay = reinterpret_cast<XCloseDisplayFn>(dlsym(x11Lib, "XCloseDisplay"));
                    if (!xOpenDisplay || !xDefaultScreen || !xDefaultVisual || !xVisualIDFromVisual || !xCloseDisplay) {
                        dlclose(x11Lib);
                        return 0;
                    }

                    void* display = xOpenDisplay(displayName);
                    if (!display) {
                        dlclose(x11Lib);
                        return 0;
                    }

                    const int screen = xDefaultScreen(display);
                    void* visual = xDefaultVisual(display, screen);
                    const auto visualId = visual ? static_cast<EGLint>(xVisualIDFromVisual(visual)) : 0;
                    xCloseDisplay(display);
                    dlclose(x11Lib);
                    return visualId;
#else
                    return 0;
#endif
                }
            } // namespace

            Bool EGLContext::DisplayLookupKey::operator==(const DisplayLookupKey& rhs) const {
                return this->NativeDisplayKey == rhs.NativeDisplayKey && this->Platform == rhs.Platform;
            }

            SizeT EGLContext::DisplayLookupHasher::operator()(const DisplayLookupKey& key) const {
                const auto nativeHash = std::hash<Uint64>{}(key.NativeDisplayKey);
                const auto platformHash = std::hash<Uint64>{}(static_cast<Uint64>(key.Platform));
                return nativeHash ^ (platformHash + 0x9e3779b97f4a7c15ull + (nativeHash << 6) + (nativeHash >> 2));
            }

            std::thread::id EGLContext::CurrentThreadKey() {
                return std::this_thread::get_id();
            }

            void EGLContext::SetError(EGLint errorCode) {
                if (errorCode == EGL_SUCCESS) {
                    return;
                }
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                m_threadErrors[CurrentThreadKey()] = errorCode;
            }

            EGLint EGLContext::ConsumeError() {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto threadKey = CurrentThreadKey();
                auto errorIt = m_threadErrors.find(threadKey);
                if (errorIt == m_threadErrors.end()) {
                    return EGL_SUCCESS;
                }

                const EGLint error = errorIt->second;
                m_threadErrors.erase(errorIt);
                return error;
            }

            void EGLContext::SetBoundAPI(EGLenum api) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                m_threadBoundAPI[CurrentThreadKey()] = api;
            }

            EGLenum EGLContext::GetBoundAPI() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto apiIt = m_threadBoundAPI.find(CurrentThreadKey());
                if (apiIt == m_threadBoundAPI.end()) {
                    return EGL_OPENGL_API;
                }
                return apiIt->second;
            }

            EGLContext::EGLDisplayHandle EGLContext::GetOrCreateDisplay(Uint64 nativeDisplayKey, EGLenum platform) {
                const DisplayLookupKey key = {
                    .NativeDisplayKey = nativeDisplayKey,
                    .Platform = platform,
                };

                auto lookupIt = m_displayLookup.find(key);
                if (lookupIt != m_displayLookup.end()) {
                    return lookupIt->second;
                }

                const auto display = EncodeHandle<EGLDisplayHandle>(m_nextDisplayHandle++);
                DisplayObject displayObject = {
                    .NativeDisplayKey = nativeDisplayKey,
                    .Platform = platform,
                    .Initialized = false,
                    .MajorVersion = EGL_DISPLAY_MAJOR_VERSION,
                    .MinorVersion = EGL_DISPLAY_MINOR_VERSION,
                    .SwapInterval = 1,
                };

                displayObject.Configs.push_back(CreateDefaultConfig(display, 1, 0));
                displayObject.Configs.push_back(CreateDefaultConfig(display, 2, 8));

                m_displays[display] = displayObject;
                m_displayLookup[key] = display;
                return display;
            }

            EGLContext::EGLConfigHandle EGLContext::CreateDefaultConfig(EGLDisplayHandle display, EGLint configId,
                                                                        EGLint stencilSize) {
                const auto config = EncodeHandle<EGLConfigHandle>(m_nextConfigHandle++);
                ConfigObject cfg = {
                    .Display = display,
                    .ConfigId = configId,
                    .RedSize = 8,
                    .GreenSize = 8,
                    .BlueSize = 8,
                    .AlphaSize = 8,
                    .DepthSize = 24,
                    .StencilSize = stencilSize,
                    .SurfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT | EGL_PIXMAP_BIT,
                    .RenderableType = EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT,
                    .MinSwapInterval = 0,
                    .MaxSwapInterval = 4,
                    .NativeVisualId = QueryDefaultX11VisualId(),
                };
#if defined(ANDROID) || defined(__ANDROID__)
                cfg.NativeVisualId = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
#endif
                m_configs[config] = cfg;
                return config;
            }

            EGLContext::DisplayObject* EGLContext::TryGetDisplay(EGLDisplayHandle display) {
                auto it = m_displays.find(display);
                return it == m_displays.end() ? nullptr : &it->second;
            }

            const EGLContext::DisplayObject* EGLContext::TryGetDisplay(EGLDisplayHandle display) const {
                auto it = m_displays.find(display);
                return it == m_displays.end() ? nullptr : &it->second;
            }

            const EGLContext::ConfigObject* EGLContext::TryGetConfig(EGLConfigHandle config) const {
                auto it = m_configs.find(config);
                return it == m_configs.end() ? nullptr : &it->second;
            }

            const EGLContext::ContextObject* EGLContext::TryGetContext(EGLContextHandle context) const {
                auto it = m_contexts.find(context);
                return it == m_contexts.end() ? nullptr : &it->second;
            }

            const EGLContext::SurfaceObject* EGLContext::TryGetSurface(EGLSurfaceHandle surface) const {
                auto it = m_surfaces.find(surface);
                return it == m_surfaces.end() ? nullptr : &it->second;
            }

            const EGLContext::SyncObject* EGLContext::TryGetSync(EGLSyncHandle sync) const {
                auto it = m_syncs.find(sync);
                return it == m_syncs.end() ? nullptr : &it->second;
            }

            const EGLContext::ImageObject* EGLContext::TryGetImage(EGLImageHandle image) const {
                auto it = m_images.find(image);
                return it == m_images.end() ? nullptr : &it->second;
            }

            void EGLContext::ReleaseThreadUnlocked(const std::thread::id& threadKey) {
                auto currentIt = m_threadCurrents.find(threadKey);
                if (currentIt == m_threadCurrents.end()) {
                    return;
                }

                const EGLSurfaceHandle drawSurface = currentIt->second.DrawSurface;
                const EGLSurfaceHandle readSurface = currentIt->second.ReadSurface;
                const EGLContextHandle context = currentIt->second.Context;
                if (context != nullptr) {
                    auto ownerIt = m_contextOwners.find(context);
                    if (ownerIt != m_contextOwners.end() && ownerIt->second == threadKey) {
                        m_contextOwners.erase(ownerIt);
                    }
                }
                m_threadCurrents.erase(currentIt);
                DestroyPendingSurfaceIfUnused(drawSurface);
                DestroyPendingSurfaceIfUnused(readSurface);
            }

            Bool EGLContext::IsSurfaceCurrentUnlocked(EGLSurfaceHandle surface) const {
                if (surface == EGL_NO_SURFACE) {
                    return false;
                }
                for (const auto& current : m_threadCurrents) {
                    if (current.second.DrawSurface == surface || current.second.ReadSurface == surface) {
                        return true;
                    }
                }
                return false;
            }

            void EGLContext::DestroyPendingSurfaceIfUnused(EGLSurfaceHandle surface) {
                auto surfaceIt = m_surfaces.find(surface);
                if (surfaceIt == m_surfaces.end() || !surfaceIt->second.DestroyPending ||
                    IsSurfaceCurrentUnlocked(surface)) {
                    return;
                }
                m_surfaces.erase(surfaceIt);
            }

            void EGLContext::ReleaseDisplayObjects(EGLDisplayHandle display) {
                Vector<EGLContextHandle> removedContexts;
                for (auto contextIt = m_contexts.begin(); contextIt != m_contexts.end();) {
                    if (contextIt->second.Display == display) {
                        removedContexts.push_back(contextIt->first);
                        contextIt = m_contexts.erase(contextIt);
                    } else {
                        ++contextIt;
                    }
                }

                for (const auto context : removedContexts) {
                    m_contextOwners.erase(context);
                }

                for (auto surfaceIt = m_surfaces.begin(); surfaceIt != m_surfaces.end();) {
                    if (surfaceIt->second.Display == display) {
                        surfaceIt = m_surfaces.erase(surfaceIt);
                    } else {
                        ++surfaceIt;
                    }
                }

                for (auto syncIt = m_syncs.begin(); syncIt != m_syncs.end();) {
                    if (syncIt->second.Display == display) {
                        syncIt = m_syncs.erase(syncIt);
                    } else {
                        ++syncIt;
                    }
                }

                for (auto imageIt = m_images.begin(); imageIt != m_images.end();) {
                    if (imageIt->second.Display == display) {
                        imageIt = m_images.erase(imageIt);
                    } else {
                        ++imageIt;
                    }
                }

                for (auto threadIt = m_threadCurrents.begin(); threadIt != m_threadCurrents.end();) {
                    if (threadIt->second.Display == display) {
                        if (threadIt->second.Context != nullptr) {
                            auto ownerIt = m_contextOwners.find(threadIt->second.Context);
                            if (ownerIt != m_contextOwners.end() && ownerIt->second == threadIt->first) {
                                m_contextOwners.erase(ownerIt);
                            }
                        }
                        threadIt = m_threadCurrents.erase(threadIt);
                    } else {
                        ++threadIt;
                    }
                }
            }

            EGLContext::EGLDisplayHandle EGLContext::GetDisplay(NativeDisplayType nativeDisplay) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return GetOrCreateDisplay(ToNativeKey(nativeDisplay), EGL_NONE);
            }

            EGLContext::EGLDisplayHandle EGLContext::GetPlatformDisplay(EGLenum platform, void* nativeDisplay) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return GetOrCreateDisplay(ToNativeKey(nativeDisplay), platform);
            }

            Bool EGLContext::ValidateDisplay(EGLDisplayHandle display) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return m_displays.find(display) != m_displays.end();
            }

            Bool EGLContext::IsDisplayInitialized(EGLDisplayHandle display) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto* displayObject = TryGetDisplay(display);
                return displayObject && displayObject->Initialized;
            }

            Bool EGLContext::InitializeDisplay(EGLDisplayHandle display, EGLint* major, EGLint* minor) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto* displayObject = TryGetDisplay(display);
                if (!displayObject) {
                    SetError(EGL_BAD_DISPLAY);
                    return false;
                }

                displayObject->Initialized = true;
                displayObject->MajorVersion = EGL_DISPLAY_MAJOR_VERSION;
                displayObject->MinorVersion = EGL_DISPLAY_MINOR_VERSION;

                if (major) {
                    *major = displayObject->MajorVersion;
                }
                if (minor) {
                    *minor = displayObject->MinorVersion;
                }
                return true;
            }

            Bool EGLContext::TerminateDisplay(EGLDisplayHandle display) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto* displayObject = TryGetDisplay(display);
                if (!displayObject) {
                    SetError(EGL_BAD_DISPLAY);
                    return false;
                }

                ReleaseDisplayObjects(display);
                displayObject->Initialized = false;
                return true;
            }

            Bool EGLContext::HasAnyInitializedDisplay() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                for (const auto& [handle, displayObject] : m_displays) {
                    if (displayObject.Initialized) {
                        return true;
                    }
                }
                return false;
            }

            Bool EGLContext::HasAnyCurrentContext() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                for (const auto& [threadId, current] : m_threadCurrents) {
                    if (current.Context != nullptr) {
                        return true;
                    }
                }
                return false;
            }

            Bool EGLContext::ChooseConfig(EGLDisplayHandle display, const EGLint* attribList, EGLConfigHandle* configs,
                                          EGLint configSize, EGLint* numConfig) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto* displayObject = TryGetDisplay(display);
                if (!displayObject) {
                    SetError(EGL_BAD_DISPLAY);
                    return false;
                }
                if (!displayObject->Initialized) {
                    SetError(EGL_NOT_INITIALIZED);
                    return false;
                }
                if (!numConfig) {
                    SetError(EGL_BAD_PARAMETER);
                    return false;
                }

                Vector<EGLConfigHandle> matchedConfigs;
                for (const auto config : displayObject->Configs) {
                    const auto* cfg = TryGetConfig(config);
                    if (!cfg) {
                        continue;
                    }

                    Bool matches = true;
                    if (attribList) {
                        for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) {
                            const EGLint attr = attribList[i];
                            const EGLint requested = attribList[i + 1];
                            if (requested == EGL_DONT_CARE) {
                                continue;
                            }

                            switch (attr) {
                            case EGL_CONFIG_ID:
                                matches = cfg->ConfigId == requested;
                                break;
                            case EGL_RED_SIZE:
                                matches = cfg->RedSize >= requested;
                                break;
                            case EGL_GREEN_SIZE:
                                matches = cfg->GreenSize >= requested;
                                break;
                            case EGL_BLUE_SIZE:
                                matches = cfg->BlueSize >= requested;
                                break;
                            case EGL_ALPHA_SIZE:
                                matches = cfg->AlphaSize >= requested;
                                break;
                            case EGL_BUFFER_SIZE:
                                matches = (cfg->RedSize + cfg->GreenSize + cfg->BlueSize + cfg->AlphaSize) >= requested;
                                break;
                            case EGL_DEPTH_SIZE:
                                matches = cfg->DepthSize >= requested;
                                break;
                            case EGL_STENCIL_SIZE:
                                matches = cfg->StencilSize >= requested;
                                break;
                            case EGL_SURFACE_TYPE:
                                matches = (cfg->SurfaceType & requested) == requested;
                                break;
                            case EGL_RENDERABLE_TYPE:
                            case EGL_CONFORMANT:
                                matches = (cfg->RenderableType & requested) == requested;
                                break;
                            case EGL_SAMPLE_BUFFERS:
                            case EGL_SAMPLES:
                                matches = requested == 0;
                                break;
                            case EGL_NATIVE_VISUAL_ID:
                                matches = cfg->NativeVisualId == requested;
                                break;
                            default:
                                break;
                            }

                            if (!matches) {
                                break;
                            }
                        }
                    }

                    if (matches) {
                        matchedConfigs.push_back(config);
                    }
                }

                *numConfig = static_cast<EGLint>(matchedConfigs.size());
                if (!configs || configSize <= 0) {
                    return true;
                }

                const SizeT copyCount = std::min(static_cast<SizeT>(configSize), matchedConfigs.size());
                for (SizeT i = 0; i < copyCount; ++i) {
                    configs[i] = matchedConfigs[i];
                }
                return true;
            }

            Bool EGLContext::GetConfigs(EGLDisplayHandle display, EGLConfigHandle* configs, EGLint configSize,
                                        EGLint* numConfig) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return ChooseConfig(display, nullptr, configs, configSize, numConfig);
            }

            Bool EGLContext::ValidateConfig(EGLConfigHandle config) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return m_configs.find(config) != m_configs.end();
            }

            Bool EGLContext::ValidateConfigOnDisplay(EGLDisplayHandle display, EGLConfigHandle config) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto* cfg = TryGetConfig(config);
                return cfg && cfg->Display == display;
            }

            Bool EGLContext::GetConfigAttrib(EGLDisplayHandle display, EGLConfigHandle config, EGLint attribute,
                                             EGLint* value) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!value) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                const auto* cfg = TryGetConfig(config);
                if (!cfg) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONFIG);
                    return false;
                }
                if (cfg->Display != display) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
                    return false;
                }

                switch (attribute) {
                case EGL_BUFFER_SIZE:
                    *value = cfg->RedSize + cfg->GreenSize + cfg->BlueSize + cfg->AlphaSize;
                    return true;
                case EGL_ALPHA_MASK_SIZE:
                    *value = 0;
                    return true;
                case EGL_BIND_TO_TEXTURE_RGB:
                case EGL_BIND_TO_TEXTURE_RGBA:
                    *value = EGL_FALSE;
                    return true;
                case EGL_COLOR_BUFFER_TYPE:
                    *value = EGL_RGB_BUFFER;
                    return true;
                case EGL_CONFIG_CAVEAT:
                    *value = EGL_NONE;
                    return true;
                case EGL_CONFIG_ID:
                    *value = cfg->ConfigId;
                    return true;
                case EGL_LEVEL:
                    *value = 0;
                    return true;
                case EGL_LUMINANCE_SIZE:
                    *value = 0;
                    return true;
                case EGL_MAX_PBUFFER_WIDTH:
                case EGL_MAX_PBUFFER_HEIGHT:
                    *value = DEFAULT_MAX_PBUFFER_SIZE;
                    return true;
                case EGL_MAX_PBUFFER_PIXELS:
                    *value = DEFAULT_MAX_PBUFFER_SIZE * DEFAULT_MAX_PBUFFER_SIZE;
                    return true;
                case EGL_RED_SIZE:
                    *value = cfg->RedSize;
                    return true;
                case EGL_GREEN_SIZE:
                    *value = cfg->GreenSize;
                    return true;
                case EGL_BLUE_SIZE:
                    *value = cfg->BlueSize;
                    return true;
                case EGL_ALPHA_SIZE:
                    *value = cfg->AlphaSize;
                    return true;
                case EGL_DEPTH_SIZE:
                    *value = cfg->DepthSize;
                    return true;
                case EGL_STENCIL_SIZE:
                    *value = cfg->StencilSize;
                    return true;
                case EGL_SURFACE_TYPE:
                    *value = cfg->SurfaceType;
                    return true;
                case EGL_RENDERABLE_TYPE:
                case EGL_CONFORMANT:
                    *value = cfg->RenderableType;
                    return true;
                case EGL_MIN_SWAP_INTERVAL:
                    *value = cfg->MinSwapInterval;
                    return true;
                case EGL_MAX_SWAP_INTERVAL:
                    *value = cfg->MaxSwapInterval;
                    return true;
                case EGL_NATIVE_RENDERABLE:
                    *value = EGL_TRUE;
                    return true;
                case EGL_NATIVE_VISUAL_ID:
                    *value = cfg->NativeVisualId;
                    return true;
                case EGL_NATIVE_VISUAL_TYPE:
                    *value = cfg->NativeVisualId;
                    return true;
                case EGL_SAMPLE_BUFFERS:
                case EGL_SAMPLES:
                    *value = 0;
                    return true;
                case EGL_TRANSPARENT_TYPE:
                    *value = EGL_NONE;
                    return true;
                case EGL_TRANSPARENT_RED_VALUE:
                case EGL_TRANSPARENT_GREEN_VALUE:
                case EGL_TRANSPARENT_BLUE_VALUE:
                    *value = 0;
                    return true;
                default:
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
            }

            EGLContext::EGLContextHandle EGLContext::CreateContext(EGLDisplayHandle display, EGLConfigHandle config,
                                                                   EGLContextHandle shareCtx,
                                                                   const EGLint* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto* displayObject = TryGetDisplay(display);
                if (!displayObject) {
                    SetError(EGL_BAD_DISPLAY);
                    return nullptr;
                }
                if (!displayObject->Initialized) {
                    SetError(EGL_NOT_INITIALIZED);
                    return nullptr;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return nullptr;
                }
                if (shareCtx != nullptr && !ValidateContextOnDisplay(display, shareCtx)) {
                    SetError(EGL_BAD_CONTEXT);
                    return nullptr;
                }

                ContextObject contextObject = {
                    .Display = display,
                    .Config = config,
                    .SharedContext = shareCtx,
                    .ClientAPI = GetBoundAPI(),
                    .ClientVersion = 1,
                    .MajorVersion = 1,
                    .MinorVersion = 0,
                };

                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_CLIENT_VERSION); value) {
                    contextObject.ClientVersion = *value;
                    contextObject.MajorVersion = *value;
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_MAJOR_VERSION); value) {
                    contextObject.MajorVersion = *value;
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_MINOR_VERSION); value) {
                    contextObject.MinorVersion = *value;
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_PROFILE_MASK); value) {
                    contextObject.OpenGLProfileMask = *value;
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_FLAGS_KHR); value) {
                    contextObject.EGLContextFlags = *value;
                    if (*value & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) {
                        contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
                    }
                    if (*value & EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR) {
                        contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_DEBUG_BIT;
                    }
                    if (*value & EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR) {
                        contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT;
                    }
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE);
                    value && *value == EGL_TRUE) {
                    contextObject.EGLContextFlags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
                    contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_DEBUG); value && *value == EGL_TRUE) {
                    contextObject.EGLContextFlags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
                    contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_DEBUG_BIT;
                }
                if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_ROBUST_ACCESS);
                    value && *value == EGL_TRUE) {
                    contextObject.EGLContextFlags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
                    contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT;
                }

                const auto context = EncodeHandle<EGLContextHandle>(m_nextContextHandle++);
                m_contexts[context] = contextObject;
                return context;
            }

            Bool EGLContext::DestroyContext(EGLDisplayHandle display, EGLContextHandle context) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto contextIt = m_contexts.find(context);
                if (contextIt == m_contexts.end()) {
                    SetError(EGL_BAD_CONTEXT);
                    return false;
                }
                if (contextIt->second.Display != display) {
                    SetError(EGL_BAD_MATCH);
                    return false;
                }

                auto ownerIt = m_contextOwners.find(context);
                if (ownerIt != m_contextOwners.end()) {
                    SetError(EGL_BAD_ACCESS);
                    return false;
                }
                m_contexts.erase(contextIt);
                return true;
            }

            Bool EGLContext::QueryContext(EGLDisplayHandle display, EGLContextHandle context, EGLint attribute,
                                          EGLint* value) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!value) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                const auto* contextObject = TryGetContext(context);
                if (!contextObject) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONTEXT);
                    return false;
                }
                if (contextObject->Display != display) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
                    return false;
                }

                switch (attribute) {
                case EGL_CONTEXT_CLIENT_TYPE:
                    *value = static_cast<EGLint>(contextObject->ClientAPI);
                    return true;
                case EGL_CONTEXT_CLIENT_VERSION:
#if EGL_CONTEXT_MAJOR_VERSION != EGL_CONTEXT_CLIENT_VERSION
                case EGL_CONTEXT_MAJOR_VERSION:
#endif
                    *value = contextObject->MajorVersion;
                    return true;
                case EGL_CONTEXT_MINOR_VERSION:
                    *value = contextObject->MinorVersion;
                    return true;
                case EGL_CONTEXT_FLAGS_KHR:
                    *value = contextObject->EGLContextFlags;
                    return true;
                case EGL_CONFIG_ID: {
                    const auto* cfg = TryGetConfig(contextObject->Config);
                    if (!cfg) {
                        const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONFIG);
                        return false;
                    }
                    *value = cfg->ConfigId;
                    return true;
                }
                default:
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
            }

            Bool EGLContext::ValidateContext(EGLContextHandle context) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return m_contexts.find(context) != m_contexts.end();
            }

            Bool EGLContext::ValidateContextOnDisplay(EGLDisplayHandle display, EGLContextHandle context) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto* ctx = TryGetContext(context);
                return ctx && ctx->Display == display;
            }

            Bool EGLContext::IsCurrentContextOpenGLCoreProfile() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto currentIt = m_threadCurrents.find(CurrentThreadKey());
                if (currentIt == m_threadCurrents.end()) {
                    return false;
                }
                const auto* ctx = TryGetContext(currentIt->second.Context);
                if (!ctx || ctx->ClientAPI != EGL_OPENGL_API ||
                    (ctx->OpenGLProfileMask & EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT)) {
                    return false;
                }
                return (ctx->OpenGLProfileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT) ||
                       ctx->MajorVersion > 3 || (ctx->MajorVersion == 3 && ctx->MinorVersion >= 1);
            }

            Bool EGLContext::IsCurrentContextOpenGLCompatibilityProfile() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto currentIt = m_threadCurrents.find(CurrentThreadKey());
                if (currentIt == m_threadCurrents.end()) {
                    return false;
                }
                const auto* ctx = TryGetContext(currentIt->second.Context);
                // Affirmative check: true only when the host explicitly requested the
                // compatibility bit. Attrib-less contexts report false and thus read as core
                // for GL_CONTEXT_PROFILE_MASK (EGL defaults 3.x contexts to the core profile).
                return ctx && ctx->ClientAPI == EGL_OPENGL_API &&
                       (ctx->OpenGLProfileMask & EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
            }

            EGLint EGLContext::GetCurrentContextFlags() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto currentIt = m_threadCurrents.find(CurrentThreadKey());
                if (currentIt == m_threadCurrents.end()) {
                    return 0;
                }
                const auto* ctx = TryGetContext(currentIt->second.Context);
                return ctx ? ctx->OpenGLContextFlags : 0;
            }

            EGLContext::EGLSurfaceHandle EGLContext::CreateWindowSurface(EGLDisplayHandle display,
                                                                         EGLConfigHandle config,
                                                                         NativeWindowType window,
                                                                         const EGLint* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                (void)attribList;
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SURFACE;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return EGL_NO_SURFACE;
                }
                if (ToNativeKey(window) == 0) {
                    SetError(EGL_BAD_NATIVE_WINDOW);
                    return EGL_NO_SURFACE;
                }

                const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
                m_surfaces[surface] = SurfaceObject{
                    .Display = display,
                    .Config = config,
                    .Type = SurfaceType::Window,
                    .NativeHandleKey = ToNativeKey(window),
                    .ClientBuffer = nullptr,
                    .BufferType = EGL_NONE,
                };
                return surface;
            }

            EGLContext::EGLSurfaceHandle EGLContext::CreatePbufferSurface(EGLDisplayHandle display,
                                                                          EGLConfigHandle config,
                                                                          const EGLint* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SURFACE;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return EGL_NO_SURFACE;
                }

                EGLint width = 1;
                EGLint height = 1;
                if (auto value = ParseAttribValue(attribList, EGL_WIDTH); value) {
                    width = *value;
                }
                if (auto value = ParseAttribValue(attribList, EGL_HEIGHT); value) {
                    height = *value;
                }
                if (width < 0 || height < 0) {
                    SetError(EGL_BAD_ATTRIBUTE);
                    return EGL_NO_SURFACE;
                }

                const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
                m_surfaces[surface] = SurfaceObject{
                    .Display = display,
                    .Config = config,
                    .Type = SurfaceType::Pbuffer,
                    .NativeHandleKey = 0,
                    .ClientBuffer = nullptr,
                    .BufferType = EGL_NONE,
                    .Width = width,
                    .Height = height,
                    .TextureFormat = ParseAttribValue(attribList, EGL_TEXTURE_FORMAT).value_or(EGL_NO_TEXTURE),
                    .TextureTarget = ParseAttribValue(attribList, EGL_TEXTURE_TARGET).value_or(EGL_NO_TEXTURE),
                    .MipmapLevel = ParseAttribValue(attribList, EGL_MIPMAP_LEVEL).value_or(0),
                    .MipmapTexture = ParseAttribValue(attribList, EGL_MIPMAP_TEXTURE).value_or(EGL_FALSE),
                };
                return surface;
            }

            EGLContext::EGLSurfaceHandle EGLContext::CreatePixmapSurface(EGLDisplayHandle display,
                                                                         EGLConfigHandle config,
                                                                         EGLNativePixmapType pixmap,
                                                                         const EGLint* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                (void)attribList;
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SURFACE;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return EGL_NO_SURFACE;
                }
                if (ToNativeKey(pixmap) == 0) {
                    SetError(EGL_BAD_NATIVE_PIXMAP);
                    return EGL_NO_SURFACE;
                }

                const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
                m_surfaces[surface] = SurfaceObject{
                    .Display = display,
                    .Config = config,
                    .Type = SurfaceType::Pixmap,
                    .NativeHandleKey = ToNativeKey(pixmap),
                    .ClientBuffer = nullptr,
                    .BufferType = EGL_NONE,
                };
                return surface;
            }

            EGLContext::EGLSurfaceHandle EGLContext::CreatePbufferFromClientBuffer(EGLDisplayHandle display,
                                                                                   EGLenum bufferType,
                                                                                   EGLClientBuffer buffer,
                                                                                   EGLConfigHandle config,
                                                                                   const EGLint* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SURFACE;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return EGL_NO_SURFACE;
                }
                if (!buffer) {
                    SetError(EGL_BAD_PARAMETER);
                    return EGL_NO_SURFACE;
                }

                EGLint width = 1;
                EGLint height = 1;
                if (auto value = ParseAttribValue(attribList, EGL_WIDTH); value) {
                    width = *value;
                }
                if (auto value = ParseAttribValue(attribList, EGL_HEIGHT); value) {
                    height = *value;
                }

                const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
                m_surfaces[surface] = SurfaceObject{
                    .Display = display,
                    .Config = config,
                    .Type = SurfaceType::PbufferFromClientBuffer,
                    .NativeHandleKey = 0,
                    .ClientBuffer = buffer,
                    .BufferType = bufferType,
                    .Width = width,
                    .Height = height,
                };
                return surface;
            }

            EGLContext::EGLSurfaceHandle EGLContext::CreatePlatformWindowSurface(EGLDisplayHandle display,
                                                                                 EGLConfigHandle config,
                                                                                 void* nativeWindow,
                                                                                 const EGLAttrib* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SURFACE;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return EGL_NO_SURFACE;
                }
                if (ToNativeKey(nativeWindow) == 0) {
                    SetError(EGL_BAD_NATIVE_WINDOW);
                    return EGL_NO_SURFACE;
                }

                const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
                m_surfaces[surface] = SurfaceObject{
                    .Display = display,
                    .Config = config,
                    .Type = SurfaceType::PlatformWindow,
                    .NativeHandleKey = ToNativeKey(nativeWindow),
                    .ClientBuffer = nullptr,
                    .BufferType = EGL_NONE,
                    .Width = static_cast<EGLint>(ParseAttribValue(attribList, EGL_WIDTH).value_or(0)),
                    .Height = static_cast<EGLint>(ParseAttribValue(attribList, EGL_HEIGHT).value_or(0)),
                };
                return surface;
            }

            EGLContext::EGLSurfaceHandle EGLContext::CreatePlatformPixmapSurface(EGLDisplayHandle display,
                                                                                 EGLConfigHandle config,
                                                                                 void* nativePixmap,
                                                                                 const EGLAttrib* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                (void)attribList;
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SURFACE;
                }
                if (!ValidateConfigOnDisplay(display, config)) {
                    SetError(EGL_BAD_CONFIG);
                    return EGL_NO_SURFACE;
                }
                if (ToNativeKey(nativePixmap) == 0) {
                    SetError(EGL_BAD_NATIVE_PIXMAP);
                    return EGL_NO_SURFACE;
                }

                const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
                m_surfaces[surface] = SurfaceObject{
                    .Display = display,
                    .Config = config,
                    .Type = SurfaceType::PlatformPixmap,
                    .NativeHandleKey = ToNativeKey(nativePixmap),
                    .ClientBuffer = nullptr,
                    .BufferType = EGL_NONE,
                };
                return surface;
            }

            Bool EGLContext::DestroySurface(EGLDisplayHandle display, EGLSurfaceHandle surface) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto surfaceIt = m_surfaces.find(surface);
                if (surfaceIt == m_surfaces.end()) {
                    SetError(EGL_BAD_SURFACE);
                    return false;
                }
                if (surfaceIt->second.Display != display) {
                    SetError(EGL_BAD_MATCH);
                    return false;
                }

                if (IsSurfaceCurrentUnlocked(surface)) {
                    surfaceIt->second.DestroyPending = true;
                    return true;
                }
                m_surfaces.erase(surfaceIt);
                return true;
            }

            Bool EGLContext::ResizeSurface(EGLDisplayHandle display, EGLSurfaceHandle surface,
                                           EGLint width, EGLint height) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto surfaceIt = m_surfaces.find(surface);
                if (surfaceIt == m_surfaces.end()) {
                    SetError(EGL_BAD_SURFACE);
                    return false;
                }
                if (surfaceIt->second.Display != display) {
                    SetError(EGL_BAD_MATCH);
                    return false;
                }
                surfaceIt->second.Width = std::max<EGLint>(width, 1);
                surfaceIt->second.Height = std::max<EGLint>(height, 1);
                return true;
            }

            Bool EGLContext::QuerySurface(EGLDisplayHandle display, EGLSurfaceHandle surface, EGLint attribute,
                                          EGLint* value) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!value) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                const auto* surfaceObject = TryGetSurface(surface);
                if (!surfaceObject) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_SURFACE);
                    return false;
                }
                if (surfaceObject->Display != display) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
                    return false;
                }

                switch (attribute) {
                case EGL_WIDTH:
                    *value = surfaceObject->Width;
                    return true;
                case EGL_HEIGHT:
                    *value = surfaceObject->Height;
                    return true;
                case EGL_TEXTURE_FORMAT:
                    *value = surfaceObject->TextureFormat;
                    return true;
                case EGL_TEXTURE_TARGET:
                    *value = surfaceObject->TextureTarget;
                    return true;
                case EGL_MIPMAP_LEVEL:
                    *value = surfaceObject->MipmapLevel;
                    return true;
                case EGL_MIPMAP_TEXTURE:
                    *value = surfaceObject->MipmapTexture;
                    return true;
                case EGL_RENDER_BUFFER:
                    *value = surfaceObject->RenderBuffer;
                    return true;
                case EGL_SWAP_BEHAVIOR:
                    *value = surfaceObject->SwapBehavior;
                    return true;
                case EGL_CONFIG_ID: {
                    const auto* cfg = TryGetConfig(surfaceObject->Config);
                    if (!cfg) {
                        const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONFIG);
                        return false;
                    }
                    *value = cfg->ConfigId;
                    return true;
                }
                default:
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
            }

            Bool EGLContext::ValidateSurface(EGLSurfaceHandle surface) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                return m_surfaces.find(surface) != m_surfaces.end();
            }

            Bool EGLContext::ValidateSurfaceOnDisplay(EGLDisplayHandle display, EGLSurfaceHandle surface) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto* surf = TryGetSurface(surface);
                return surf && surf->Display == display;
            }

            Bool EGLContext::SwapInterval(EGLDisplayHandle display, EGLint interval) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto* displayObject = TryGetDisplay(display);
                if (!displayObject) {
                    SetError(EGL_BAD_DISPLAY);
                    return false;
                }
                if (!displayObject->Initialized) {
                    SetError(EGL_NOT_INITIALIZED);
                    return false;
                }

                if (displayObject->Configs.empty()) {
                    SetError(EGL_BAD_CONFIG);
                    return false;
                }
                const auto* cfg = TryGetConfig(displayObject->Configs.front());
                if (!cfg) {
                    SetError(EGL_BAD_CONFIG);
                    return false;
                }
                if (interval < cfg->MinSwapInterval || interval > cfg->MaxSwapInterval) {
                    SetError(EGL_BAD_PARAMETER);
                    return false;
                }

                displayObject->SwapInterval = interval;
                return true;
            }

            Bool EGLContext::MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read,
                                         EGLContextHandle context) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const Bool releaseCurrentRequest =
                    draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && context == nullptr;
                const auto threadKey = CurrentThreadKey();
                if (releaseCurrentRequest) {
                    if (display != EGL_NO_DISPLAY) {
                        auto* displayObject = TryGetDisplay(display);
                        if (!displayObject) {
                            SetError(EGL_BAD_DISPLAY);
                            return false;
                        }
                        if (!displayObject->Initialized) {
                            SetError(EGL_NOT_INITIALIZED);
                            return false;
                        }
                    }
                    ReleaseThreadUnlocked(threadKey);
                    return true;
                }

                auto* displayObject = TryGetDisplay(display);
                if (!displayObject) {
                    SetError(EGL_BAD_DISPLAY);
                    return false;
                }
                if (!displayObject->Initialized) {
                    SetError(EGL_NOT_INITIALIZED);
                    return false;
                }

                if (context != nullptr) {
                    if (!ValidateContextOnDisplay(display, context)) {
                        SetError(EGL_BAD_CONTEXT);
                        return false;
                    }
                    if (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE) {
                        SetError(EGL_BAD_MATCH);
                        return false;
                    }
                } else {
                    if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
                        SetError(EGL_BAD_MATCH);
                        return false;
                    }
                }

                if (draw != EGL_NO_SURFACE && !ValidateSurfaceOnDisplay(display, draw)) {
                    SetError(EGL_BAD_SURFACE);
                    return false;
                }
                if (read != EGL_NO_SURFACE && !ValidateSurfaceOnDisplay(display, read)) {
                    SetError(EGL_BAD_SURFACE);
                    return false;
                }

                if (context != nullptr) {
                    auto ownerIt = m_contextOwners.find(context);
                    if (ownerIt != m_contextOwners.end() && ownerIt->second != threadKey) {
                        SetError(EGL_BAD_ACCESS);
                        return false;
                    }
                }

                ReleaseThreadUnlocked(threadKey);

                m_threadCurrents[threadKey] = ThreadCurrentState{
                    .Display = display,
                    .DrawSurface = draw,
                    .ReadSurface = read,
                    .Context = context,
                };
                if (context != nullptr) {
                    m_contextOwners[context] = threadKey;
                }
                return true;
            }

            void EGLContext::ReleaseThread() {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                ReleaseThreadUnlocked(CurrentThreadKey());
            }

            EGLContext::EGLContextHandle EGLContext::GetCurrentContext() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto currentIt = m_threadCurrents.find(CurrentThreadKey());
                if (currentIt == m_threadCurrents.end()) {
                    return nullptr;
                }
                return currentIt->second.Context;
            }

            EGLContext::EGLDisplayHandle EGLContext::GetCurrentDisplay() const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto currentIt = m_threadCurrents.find(CurrentThreadKey());
                if (currentIt == m_threadCurrents.end()) {
                    return EGL_NO_DISPLAY;
                }
                return currentIt->second.Display;
            }

            EGLContext::EGLSurfaceHandle EGLContext::GetCurrentSurface(EGLint readdraw) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto currentIt = m_threadCurrents.find(CurrentThreadKey());
                if (currentIt == m_threadCurrents.end()) {
                    return EGL_NO_SURFACE;
                }
                if (readdraw == EGL_DRAW) {
                    return currentIt->second.DrawSurface;
                }
                if (readdraw == EGL_READ) {
                    return currentIt->second.ReadSurface;
                }
                return EGL_NO_SURFACE;
            }

            Bool EGLContext::IsDoubleBufferedSurface(EGLSurfaceHandle surface) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                const auto* surfaceObject = TryGetSurface(surface);
                if (!surfaceObject) {
                    return false;
                }

                if (surfaceObject->RenderBuffer != EGL_BACK_BUFFER) {
                    return false;
                }

                return surfaceObject->Type == SurfaceType::Window ||
                       surfaceObject->Type == SurfaceType::PlatformWindow;
            }

            EGLContext::EGLSyncHandle EGLContext::CreateSync(EGLDisplayHandle display, EGLenum type,
                                                             const EGLAttrib* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_SYNC;
                }
                if (type != EGL_SYNC_FENCE) {
                    SetError(EGL_BAD_ATTRIBUTE);
                    return EGL_NO_SYNC;
                }

                SyncObject sync = {
                    .Display = display,
                    .Type = type,
                    .Condition = EGL_SYNC_PRIOR_COMMANDS_COMPLETE,
                    .Status = EGL_SIGNALED,
                };
                if (auto value = ParseAttribValue(attribList, EGL_SYNC_CONDITION); value) {
                    sync.Condition = static_cast<EGLenum>(*value);
                }

                const auto syncHandle = EncodeHandle<EGLSyncHandle>(m_nextSyncHandle++);
                m_syncs[syncHandle] = sync;
                return syncHandle;
            }

            Bool EGLContext::DestroySync(EGLDisplayHandle display, EGLSyncHandle sync) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto syncIt = m_syncs.find(sync);
                if (syncIt == m_syncs.end()) {
                    SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                if (syncIt->second.Display != display) {
                    SetError(EGL_BAD_MATCH);
                    return false;
                }
                m_syncs.erase(syncIt);
                return true;
            }

            EGLint EGLContext::ClientWaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags,
                                              EGLTime timeout) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                (void)flags;
                (void)timeout;

                const auto* syncObject = TryGetSync(sync);
                if (!syncObject) {
                    SetError(EGL_BAD_PARAMETER);
                    return EGL_FALSE;
                }
                if (syncObject->Display != display) {
                    SetError(EGL_BAD_MATCH);
                    return EGL_FALSE;
                }

                return EGL_CONDITION_SATISFIED;
            }

            Bool EGLContext::GetSyncAttrib(EGLDisplayHandle display, EGLSyncHandle sync, EGLint attribute,
                                           EGLAttrib* value) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                if (!value) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                const auto* syncObject = TryGetSync(sync);
                if (!syncObject) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                if (syncObject->Display != display) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
                    return false;
                }

                switch (attribute) {
                case EGL_SYNC_TYPE:
                    *value = static_cast<EGLAttrib>(syncObject->Type);
                    return true;
                case EGL_SYNC_STATUS:
                    *value = static_cast<EGLAttrib>(syncObject->Status);
                    return true;
                case EGL_SYNC_CONDITION:
                    *value = static_cast<EGLAttrib>(syncObject->Condition);
                    return true;
                default:
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
                    return false;
                }
            }

            Bool EGLContext::WaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags) const {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                (void)flags;
                const auto* syncObject = TryGetSync(sync);
                if (!syncObject) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                if (syncObject->Display != display) {
                    const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
                    return false;
                }
                return true;
            }

            EGLContext::EGLImageHandle EGLContext::CreateImage(EGLDisplayHandle display, EGLContextHandle context,
                                                               EGLenum target, EGLClientBuffer buffer,
                                                               const EGLAttrib* attribList) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                (void)attribList;
                if (!IsDisplayInitialized(display)) {
                    SetError(EGL_NOT_INITIALIZED);
                    return EGL_NO_IMAGE;
                }
                if (context != nullptr && !ValidateContextOnDisplay(display, context)) {
                    SetError(EGL_BAD_CONTEXT);
                    return EGL_NO_IMAGE;
                }

                const auto image = EncodeHandle<EGLImageHandle>(m_nextImageHandle++);
                m_images[image] = ImageObject{
                    .Display = display,
                    .Context = context,
                    .Target = target,
                    .Buffer = buffer,
                };
                return image;
            }

            Bool EGLContext::DestroyImage(EGLDisplayHandle display, EGLImageHandle image) {
                const std::lock_guard<std::recursive_mutex> lock(m_mutex);
                auto imageIt = m_images.find(image);
                if (imageIt == m_images.end()) {
                    SetError(EGL_BAD_PARAMETER);
                    return false;
                }
                if (imageIt->second.Display != display) {
                    SetError(EGL_BAD_MATCH);
                    return false;
                }
                m_images.erase(imageIt);
                return true;
            }
        } // namespace EGLState

        // Leak-at-exit storage; see GlobalObjects.cpp.
        UniquePtr<EGLState::EGLContext>& pEGLContext = *new UniquePtr<EGLState::EGLContext>();
    } // namespace MG_State
} // namespace MobileGL
