// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "pch.h"
#include "MonitorTopology.h"
#include "CursorWrapCore.h" // For CursorDirection struct
#include "../../../common/logger/logger.h"
#include <algorithm>
#include <cmath>

void MonitorTopology::Initialize(const std::vector<MonitorInfo>& monitors)
{
    Logger::info(L"======= TOPOLOGY INITIALIZATION START =======");
    Logger::info(L"Initializing edge-based topology for {} monitors", monitors.size());

    m_monitors = monitors;
    m_outerEdges.clear();
    m_edgeMap.clear();

    if (monitors.empty())
    {
        Logger::warn(L"No monitors provided to Initialize");
        return;
    }

    // Log monitor details
    for (size_t i = 0; i < monitors.size(); ++i)
    {
        const auto& m = monitors[i];
        Logger::info(L"Monitor {}: hMonitor={}, rect=({},{},{},{}), primary={}",
            i, reinterpret_cast<uintptr_t>(m.hMonitor),
            m.rect.left, m.rect.top, m.rect.right, m.rect.bottom,
            m.isPrimary ? L"yes" : L"no");
    }

    BuildEdgeMap();
    IdentifyOuterEdges();

    Logger::info(L"Found {} outer edges", m_outerEdges.size());
    for (const auto& edge : m_outerEdges)
    {
        const wchar_t* typeStr = L"Unknown";
        switch (edge.type)
        {
        case EdgeType::Left: typeStr = L"Left"; break;
        case EdgeType::Right: typeStr = L"Right"; break;
        case EdgeType::Top: typeStr = L"Top"; break;
        case EdgeType::Bottom: typeStr = L"Bottom"; break;
        }
        Logger::info(L"Outer edge: Monitor {} {} at position {}, range [{}, {}]",
            edge.monitorIndex, typeStr, edge.position, edge.start, edge.end);
    }
    Logger::info(L"======= TOPOLOGY INITIALIZATION COMPLETE =======");
}

void MonitorTopology::BuildEdgeMap()
{
    // Create edges for each monitor using monitor index (not HMONITOR)
    // This is important because HMONITOR handles can change when monitors are
    // added/removed dynamically, but indices remain stable within a single
    // topology configuration
    for (size_t idx = 0; idx < m_monitors.size(); ++idx)
    {
        const auto& monitor = m_monitors[idx];
        int monitorIndex = static_cast<int>(idx);

        // Left edge
        MonitorEdge leftEdge;
        leftEdge.monitorIndex = monitorIndex;
        leftEdge.type = EdgeType::Left;
        leftEdge.position = monitor.rect.left;
        leftEdge.start = monitor.rect.top;
        leftEdge.end = monitor.rect.bottom;
        leftEdge.isOuter = true; // Will be updated in IdentifyOuterEdges
        m_edgeMap[{monitorIndex, EdgeType::Left}] = leftEdge;
      
        // Right edge
        MonitorEdge rightEdge;
        rightEdge.monitorIndex = monitorIndex;
        rightEdge.type = EdgeType::Right;
        rightEdge.position = monitor.rect.right - 1;
        rightEdge.start = monitor.rect.top;
        rightEdge.end = monitor.rect.bottom;
        rightEdge.isOuter = true;
        m_edgeMap[{monitorIndex, EdgeType::Right}] = rightEdge;

        // Top edge
        MonitorEdge topEdge;
        topEdge.monitorIndex = monitorIndex;
        topEdge.type = EdgeType::Top;
        topEdge.position = monitor.rect.top;
        topEdge.start = monitor.rect.left;
        topEdge.end = monitor.rect.right;
        topEdge.isOuter = true;
        m_edgeMap[{monitorIndex, EdgeType::Top}] = topEdge;

        // Bottom edge
        MonitorEdge bottomEdge;
        bottomEdge.monitorIndex = monitorIndex;
        bottomEdge.type = EdgeType::Bottom;
        bottomEdge.position = monitor.rect.bottom - 1;
        bottomEdge.start = monitor.rect.left;
        bottomEdge.end = monitor.rect.right;
        bottomEdge.isOuter = true;
        m_edgeMap[{monitorIndex, EdgeType::Bottom}] = bottomEdge;
    }
}

void MonitorTopology::IdentifyOuterEdges()
{
    const int tolerance = 50;
    
    // Check each edge against all other edges to find adjacent ones
    for (auto& [key1, edge1] : m_edgeMap)
    {
        for (const auto& [key2, edge2] : m_edgeMap)
        {
            if (edge1.monitorIndex == edge2.monitorIndex)
            {
                continue; // Same monitor
            }

            // Check if edges are adjacent
            if (EdgesAreAdjacent(edge1, edge2, tolerance))
            {
                edge1.isOuter = false;
                break; // This edge has an adjacent monitor
            }
        }

        if (edge1.isOuter)
        {
            m_outerEdges.push_back(edge1);
        }
    }
}

bool MonitorTopology::EdgesAreAdjacent(const MonitorEdge& edge1, const MonitorEdge& edge2, int tolerance) const
{
    // Edges must be opposite types to be adjacent
    bool oppositeTypes = false;

    if ((edge1.type == EdgeType::Left && edge2.type == EdgeType::Right) ||
        (edge1.type == EdgeType::Right && edge2.type == EdgeType::Left) ||
        (edge1.type == EdgeType::Top && edge2.type == EdgeType::Bottom) ||
        (edge1.type == EdgeType::Bottom && edge2.type == EdgeType::Top))
    {
        oppositeTypes = true;
    }

    if (!oppositeTypes)
    {
        return false;
    }

    // Check if positions are within tolerance
    if (abs(edge1.position - edge2.position) > tolerance)
    {
        return false;
    }

    // Check if perpendicular ranges overlap
    int overlapStart = max(edge1.start, edge2.start);
    int overlapEnd = min(edge1.end, edge2.end);


    return overlapEnd > overlapStart + tolerance;
}

EdgeType MonitorTopology::PrioritizeEdgeByDirection(const std::vector<EdgeType>& candidates, 
                                                     const CursorDirection* direction) const
{
    if (candidates.empty())
    {
        return EdgeType::Left; // Should not happen, but return a default
    }
    
    if (candidates.size() == 1 || direction == nullptr)
    {
        return candidates[0];
    }
    
    // Prioritize based on movement direction
    // If moving primarily horizontally, prefer horizontal edges (Left/Right)
    // If moving primarily vertically, prefer vertical edges (Top/Bottom)
    
    if (direction->IsPrimarilyHorizontal())
    {
        // Prefer Left if moving left, Right if moving right
        if (direction->IsMovingLeft())
        {
            for (EdgeType edge : candidates)
            {
                if (edge == EdgeType::Left) return edge;
            }
        }
        else if (direction->IsMovingRight())
        {
            for (EdgeType edge : candidates)
            {
                if (edge == EdgeType::Right) return edge;
            }
        }
        // Fall back to any horizontal edge
        for (EdgeType edge : candidates)
        {
            if (edge == EdgeType::Left || edge == EdgeType::Right) return edge;
        }
    }
    else
    {
        // Prefer Top if moving up, Bottom if moving down
        if (direction->IsMovingUp())
        {
            for (EdgeType edge : candidates)
            {
                if (edge == EdgeType::Top) return edge;
            }
        }
        else if (direction->IsMovingDown())
        {
            for (EdgeType edge : candidates)
            {
                if (edge == EdgeType::Bottom) return edge;
            }
        }
        // Fall back to any vertical edge
        for (EdgeType edge : candidates)
        {
            if (edge == EdgeType::Top || edge == EdgeType::Bottom) return edge;
        }
    }
    
    // Default to first candidate
    return candidates[0];
}

bool MonitorTopology::IsOnOuterEdge(HMONITOR monitor, const POINT& cursorPos, EdgeType& outEdgeType, 
                                     WrapMode wrapMode, const CursorDirection* direction) const
{
    RECT monitorRect;
    if (!GetMonitorRect(monitor, monitorRect))
    {
        Logger::warn(L"IsOnOuterEdge: GetMonitorRect failed for monitor handle {}", reinterpret_cast<uintptr_t>(monitor));
        return false;
    }

    // Get monitor index for edge map lookup
    int monitorIndex = GetMonitorIndex(monitor);
    if (monitorIndex < 0)
    {
        Logger::warn(L"IsOnOuterEdge: Monitor index not found for handle {} at cursor ({}, {})", 
            reinterpret_cast<uintptr_t>(monitor), cursorPos.x, cursorPos.y);
        return false; // Monitor not found in our list
    }

    // Check each edge type
    const int edgeThreshold = 1;

    // At corners, multiple edges may match - collect all candidates and try each
    // to find one with a valid wrap destination
    std::vector<EdgeType> candidateEdges;

    // Left edge - only if mode allows horizontal wrapping
    if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::HorizontalOnly) &&
        cursorPos.x <= monitorRect.left + edgeThreshold)
    {
        auto it = m_edgeMap.find({monitorIndex, EdgeType::Left});
        if (it != m_edgeMap.end() && it->second.isOuter)
        {
            candidateEdges.push_back(EdgeType::Left);
        }
    }

    // Right edge - only if mode allows horizontal wrapping
    if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::HorizontalOnly) &&
        cursorPos.x >= monitorRect.right - 1 - edgeThreshold)
    {
        auto it = m_edgeMap.find({monitorIndex, EdgeType::Right});
        if (it != m_edgeMap.end())
        {
            if (it->second.isOuter)
            {
                candidateEdges.push_back(EdgeType::Right);
            }
            // Debug: Log why right edge isn't outer
            else
            {
                Logger::trace(L"IsOnOuterEdge: Monitor {} right edge is NOT outer (inner edge)", monitorIndex);
            }
        }
    }

    // Top edge - only if mode allows vertical wrapping
    if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::VerticalOnly) &&
        cursorPos.y <= monitorRect.top + edgeThreshold)
    {
        auto it = m_edgeMap.find({monitorIndex, EdgeType::Top});
        if (it != m_edgeMap.end() && it->second.isOuter)
        {
            candidateEdges.push_back(EdgeType::Top);
        }
    }

    // Bottom edge - only if mode allows vertical wrapping
    if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::VerticalOnly) &&
        cursorPos.y >= monitorRect.bottom - 1 - edgeThreshold)
    {
        auto it = m_edgeMap.find({monitorIndex, EdgeType::Bottom});
        if (it != m_edgeMap.end() && it->second.isOuter)
        {
            candidateEdges.push_back(EdgeType::Bottom);
        }
    }

    if (candidateEdges.empty())
    {
        return false;
    }

    // Prioritize candidates by movement direction at corners
    EdgeType prioritizedEdge = PrioritizeEdgeByDirection(candidateEdges, direction);
    
    // Get the source edge info
    auto sourceIt = m_edgeMap.find({monitorIndex, prioritizedEdge});
    if (sourceIt == m_edgeMap.end())
    {
        return false;
    }
    
    // Use the new FindNearestOppositeEdge which handles non-overlapping regions
    int cursorCoord = (prioritizedEdge == EdgeType::Left || prioritizedEdge == EdgeType::Right) 
                      ? cursorPos.y : cursorPos.x;
    OppositeEdgeResult result = FindNearestOppositeEdge(prioritizedEdge, cursorCoord, sourceIt->second);
    
    if (result.found)
    {
        outEdgeType = prioritizedEdge;
        return true;
    }
    
    // If prioritized edge didn't work, try other candidates
    for (EdgeType candidate : candidateEdges)
    {
        if (candidate == prioritizedEdge) continue;
        
        auto it = m_edgeMap.find({monitorIndex, candidate});
        if (it == m_edgeMap.end()) continue;
        
        int coord = (candidate == EdgeType::Left || candidate == EdgeType::Right) 
                    ? cursorPos.y : cursorPos.x;
        OppositeEdgeResult altResult = FindNearestOppositeEdge(candidate, coord, it->second);
        
        if (altResult.found)
        {
            outEdgeType = candidate;
            return true;
        }
    }

    return false;
}

POINT MonitorTopology::GetWrapDestination(HMONITOR fromMonitor, const POINT& cursorPos, EdgeType edgeType) const
{
    // Get monitor index for edge map lookup
    int monitorIndex = GetMonitorIndex(fromMonitor);
    if (monitorIndex < 0)
    {
        return cursorPos; // Monitor not found
    }

    auto it = m_edgeMap.find({monitorIndex, edgeType});
    if (it == m_edgeMap.end())
    {
        return cursorPos; // Edge not found
    }

    const MonitorEdge& fromEdge = it->second;
    
    // Get cursor coordinate perpendicular to the edge
    int cursorCoord = (edgeType == EdgeType::Left || edgeType == EdgeType::Right) ? cursorPos.y : cursorPos.x;

    // Use the new FindNearestOppositeEdge which handles non-overlapping regions
    OppositeEdgeResult oppositeResult = FindNearestOppositeEdge(edgeType, cursorCoord, fromEdge);

    if (!oppositeResult.found)
    {
        // No opposite edge found, wrap within same monitor
        RECT monitorRect;
        if (GetMonitorRect(fromMonitor, monitorRect))
        {
            POINT result = cursorPos;
            switch (edgeType)
            {
            case EdgeType::Left:
                result.x = monitorRect.right - 2;
                break;
            case EdgeType::Right:
                result.x = monitorRect.left + 1;
                break;
            case EdgeType::Top:
                result.y = monitorRect.bottom - 2;
                break;
            case EdgeType::Bottom:
                result.y = monitorRect.top + 1;
                break;
            }
            return result;
        }
        return cursorPos;
    }

    // Calculate target position on opposite edge
    POINT result;

    if (edgeType == EdgeType::Left || edgeType == EdgeType::Right)
    {
        // Horizontal wrapping (Left<->Right edges)
        result.x = oppositeResult.edge.position;
        
        if (oppositeResult.requiresProjection)
        {
            // Use the pre-calculated projected coordinate for non-overlapping regions
            result.y = oppositeResult.projectedCoordinate;
        }
        else
        {
            // Overlapping region - preserve Y coordinate
            result.y = cursorPos.y;
        }
    }
    else
    {
        // Vertical wrapping (Top<->Bottom edges)
        result.y = oppositeResult.edge.position;
        
        if (oppositeResult.requiresProjection)
        {
            // Use the pre-calculated projected coordinate for non-overlapping regions
            result.x = oppositeResult.projectedCoordinate;
        }
        else
        {
            // Overlapping region - preserve X coordinate
            result.x = cursorPos.x;
        }
    }

    return result;
}

MonitorEdge MonitorTopology::FindOppositeOuterEdge(EdgeType fromEdge, int relativePosition) const
{
    EdgeType targetType;
    bool findMax; // true = find max position, false = find min position

    switch (fromEdge)
    {
    case EdgeType::Left:
        targetType = EdgeType::Right;
        findMax = true;
        break;
    case EdgeType::Right:
        targetType = EdgeType::Left;
        findMax = false;
        break;
    case EdgeType::Top:
        targetType = EdgeType::Bottom;
        findMax = true;
        break;
    case EdgeType::Bottom:
        targetType = EdgeType::Top;
        findMax = false;
        break;
    default:
        return { .monitorIndex = -1 }; // Invalid edge type
    }

    MonitorEdge result = { .monitorIndex = -1 }; // -1 indicates not found
    int extremePosition = findMax ? INT_MIN : INT_MAX;

    for (const auto& edge : m_outerEdges)
    {
        if (edge.type != targetType)
        {
            continue;
        }

        // Check if this edge overlaps with the relative position
        if (relativePosition >= edge.start && relativePosition <= edge.end)
        {
            if ((findMax && edge.position > extremePosition) ||
                (!findMax && edge.position < extremePosition))
            {
                extremePosition = edge.position;
                result = edge;
            }
        }
    }

    return result;
}

OppositeEdgeResult MonitorTopology::FindNearestOppositeEdge(EdgeType fromEdge, int cursorCoordinate,
                                                             const MonitorEdge& sourceEdge) const
{
    OppositeEdgeResult result;
    result.found = false;
    result.requiresProjection = false;
    result.projectedCoordinate = 0;
    result.edge.monitorIndex = -1;

    EdgeType targetType;
    bool findMax; // true = find max position (furthest right/bottom), false = find min (furthest left/top)

    switch (fromEdge)
    {
    case EdgeType::Left:
        targetType = EdgeType::Right;
        findMax = true;
        break;
    case EdgeType::Right:
        targetType = EdgeType::Left;
        findMax = false;
        break;
    case EdgeType::Top:
        targetType = EdgeType::Bottom;
        findMax = true;
        break;
    case EdgeType::Bottom:
        targetType = EdgeType::Top;
        findMax = false;
        break;
    default:
        return result; // Invalid edge type
    }

    // First, try to find an edge that directly overlaps the cursor coordinate
    MonitorEdge directMatch = FindOppositeOuterEdge(fromEdge, cursorCoordinate);
    if (directMatch.monitorIndex >= 0)
    {
        result.found = true;
        result.requiresProjection = false;
        result.edge = directMatch;
        result.projectedCoordinate = cursorCoordinate; // Not used, but set for completeness
        return result;
    }

    // No direct overlap - find the nearest opposite edge by coordinate distance
    // This handles the "dead zone" case where cursor is in a non-overlapping region
    
    int bestDistance = INT_MAX;
    MonitorEdge bestEdge = { .monitorIndex = -1 };
    int bestProjectedCoord = 0;

    for (const auto& edge : m_outerEdges)
    {
        if (edge.type != targetType)
        {
            continue;
        }

        // Calculate distance from cursor coordinate to this edge's range
        int distance = 0;
        int projectedCoord = 0;
        
        if (cursorCoordinate < edge.start)
        {
            // Cursor is before the edge's start - project to edge start with offset
            distance = edge.start - cursorCoordinate;
            projectedCoord = edge.start; // Clamp to edge start
        }
        else if (cursorCoordinate > edge.end)
        {
            // Cursor is after the edge's end - project to edge end with offset
            distance = cursorCoordinate - edge.end;
            projectedCoord = edge.end; // Clamp to edge end
        }
        else
        {
            // Cursor overlaps - this shouldn't happen since we checked direct match
            distance = 0;
            projectedCoord = cursorCoordinate;
        }

        // Choose the best edge: prefer closer edges, and among equals prefer extreme position
        bool isBetter = false;
        if (distance < bestDistance)
        {
            isBetter = true;
        }
        else if (distance == bestDistance && bestEdge.monitorIndex >= 0)
        {
            // Same distance - prefer the extreme position (furthest in wrap direction)
            if ((findMax && edge.position > bestEdge.position) ||
                (!findMax && edge.position < bestEdge.position))
            {
                isBetter = true;
            }
        }

        if (isBetter)
        {
            bestDistance = distance;
            bestEdge = edge;
            bestProjectedCoord = projectedCoord;
        }
    }

    if (bestEdge.monitorIndex >= 0)
    {
        result.found = true;
        result.requiresProjection = true;
        result.edge = bestEdge;
        
        // Calculate projected position using offset-from-boundary approach
        result.projectedCoordinate = CalculateProjectedPosition(cursorCoordinate, sourceEdge, bestEdge);
        
        Logger::trace(L"FindNearestOppositeEdge: Non-overlapping wrap from {} to Mon {} edge, cursor={}, projected={}",
            static_cast<int>(fromEdge), bestEdge.monitorIndex, cursorCoordinate, result.projectedCoordinate);
    }

    return result;
}

int MonitorTopology::CalculateProjectedPosition(int cursorCoordinate, const MonitorEdge& sourceEdge,
                                                 const MonitorEdge& targetEdge) const
{
    // Windows behavior for non-overlapping regions:
    // When cursor is in a region that doesn't overlap with the target edge,
    // clamp to the nearest boundary of the target edge.
    // This matches observed Windows cursor transition behavior.
    
    // Find the shared boundary region between source and target edges
    int sharedStart = max(sourceEdge.start, targetEdge.start);
    int sharedEnd = min(sourceEdge.end, targetEdge.end);
    
    if (cursorCoordinate >= sharedStart && cursorCoordinate <= sharedEnd)
    {
        // Cursor is in shared region - preserve the coordinate exactly
        return cursorCoordinate;
    }
    
    // For non-overlapping regions, clamp to the nearest boundary of the target edge
    // This matches Windows behavior where the cursor is projected to the closest
    // valid point on the destination edge
    int projectedCoord;
    
    if (cursorCoordinate < sharedStart)
    {
        // Cursor is BEFORE the shared region (e.g., above shared area)
        // Clamp to the start of the target edge (with small offset to stay within bounds)
        projectedCoord = targetEdge.start + 1;
    }
    else
    {
        // Cursor is AFTER the shared region (e.g., below shared area)
        // Clamp to the end of the target edge (with small offset to stay within bounds)
        projectedCoord = targetEdge.end - 1;
    }
    
    // Final bounds check
    projectedCoord = max(targetEdge.start, min(projectedCoord, targetEdge.end - 1));
    
    return projectedCoord;
}

double MonitorTopology::GetRelativePosition(const MonitorEdge& edge, int coordinate) const
{
    if (edge.end == edge.start)
    {
        return 0.5; // Avoid division by zero
    }

    int clamped = max(edge.start, min(coordinate, edge.end));
    // Use int64_t to avoid overflow warning C26451
    int64_t numerator = static_cast<int64_t>(clamped) - static_cast<int64_t>(edge.start);
    int64_t denominator = static_cast<int64_t>(edge.end) - static_cast<int64_t>(edge.start);
    return static_cast<double>(numerator) / static_cast<double>(denominator);
}

int MonitorTopology::GetAbsolutePosition(const MonitorEdge& edge, double relativePosition) const
{
    // Use int64_t to prevent arithmetic overflow during subtraction and multiplication
    int64_t range = static_cast<int64_t>(edge.end) - static_cast<int64_t>(edge.start);
    int64_t offset = static_cast<int64_t>(relativePosition * static_cast<double>(range));
    // Clamp result to int range before returning
    int64_t result = static_cast<int64_t>(edge.start) + offset;
    return static_cast<int>(result);
}

std::vector<MonitorTopology::GapInfo> MonitorTopology::DetectMonitorGaps() const
{
    std::vector<GapInfo> gaps;
    const int gapThreshold = 50; // Same as ADJACENCY_TOLERANCE

    // Check each pair of monitors
    for (size_t i = 0; i < m_monitors.size(); ++i)
    {
        for (size_t j = i + 1; j < m_monitors.size(); ++j)
        {
            const auto& m1 = m_monitors[i];
            const auto& m2 = m_monitors[j];

            // Check vertical overlap
            int vOverlapStart = max(m1.rect.top, m2.rect.top);
            int vOverlapEnd = min(m1.rect.bottom, m2.rect.bottom);
            int vOverlap = vOverlapEnd - vOverlapStart;

            if (vOverlap <= 0)
            {
                continue; // No vertical overlap, skip
            }

            // Check horizontal gap
            int hGap = min(abs(m1.rect.right - m2.rect.left), abs(m2.rect.right - m1.rect.left));

            if (hGap > gapThreshold)
            {
                GapInfo gap;
                gap.monitor1Index = static_cast<int>(i);
                gap.monitor2Index = static_cast<int>(j);
                gap.horizontalGap = hGap;
                gap.verticalOverlap = vOverlap;
                gaps.push_back(gap);
            }
        }
    }

    return gaps;
}

HMONITOR MonitorTopology::GetMonitorFromPoint(const POINT& pt) const
{
    return MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
}

bool MonitorTopology::GetMonitorRect(HMONITOR monitor, RECT& rect) const
{
    // First try direct HMONITOR comparison
    for (const auto& monitorInfo : m_monitors)
    {
        if (monitorInfo.hMonitor == monitor)
        {
            rect = monitorInfo.rect;
            return true;
        }
    }

    // Fallback: If direct comparison fails, try matching by current monitor info
    MONITORINFO mi{};
    mi.cbSize = sizeof(MONITORINFO);
    if (GetMonitorInfo(monitor, &mi))
    {
        for (const auto& monitorInfo : m_monitors)
        {
            if (monitorInfo.rect.left == mi.rcMonitor.left &&
                monitorInfo.rect.top == mi.rcMonitor.top &&
                monitorInfo.rect.right == mi.rcMonitor.right &&
                monitorInfo.rect.bottom == mi.rcMonitor.bottom)
            {
                rect = monitorInfo.rect;
                return true;
            }
        }
    }
    
    return false;
}

HMONITOR MonitorTopology::GetMonitorFromRect(const RECT& rect) const
{
    return MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
}

int MonitorTopology::GetMonitorIndex(HMONITOR monitor) const
{
    // First try direct HMONITOR comparison (fast and accurate)
    for (size_t i = 0; i < m_monitors.size(); ++i)
    {
        if (m_monitors[i].hMonitor == monitor)
        {
            return static_cast<int>(i);
        }
    }

    // Fallback: If direct comparison fails (e.g., handle changed after display reconfiguration),
    // try matching by position. Get the monitor's current rect and find matching stored rect.
    MONITORINFO mi{};
    mi.cbSize = sizeof(MONITORINFO);
    if (GetMonitorInfo(monitor, &mi))
    {
        for (size_t i = 0; i < m_monitors.size(); ++i)
        {
            // Match by rect bounds
            if (m_monitors[i].rect.left == mi.rcMonitor.left &&
                m_monitors[i].rect.top == mi.rcMonitor.top &&
                m_monitors[i].rect.right == mi.rcMonitor.right &&
                m_monitors[i].rect.bottom == mi.rcMonitor.bottom)
            {
                Logger::trace(L"GetMonitorIndex: Found monitor {} via rect fallback (handle changed from {} to {})", 
                    i, reinterpret_cast<uintptr_t>(m_monitors[i].hMonitor), reinterpret_cast<uintptr_t>(monitor));
                return static_cast<int>(i);
            }
        }
        
        // Log all stored monitors vs the requested one for debugging
        Logger::warn(L"GetMonitorIndex: No match found. Requested monitor rect=({},{},{},{})",
            mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right, mi.rcMonitor.bottom);
        for (size_t i = 0; i < m_monitors.size(); ++i)
        {
            Logger::warn(L"  Stored monitor {}: rect=({},{},{},{})",
                i, m_monitors[i].rect.left, m_monitors[i].rect.top, 
                m_monitors[i].rect.right, m_monitors[i].rect.bottom);
        }
    }
    else
    {
        Logger::warn(L"GetMonitorIndex: GetMonitorInfo failed for handle {}", reinterpret_cast<uintptr_t>(monitor));
    }

    return -1; // Not found
}

