// LIVE UI TUNER — paste into UnityExplorer's C# Console. Edit knobs, Run, look, repeat.
// Nothing persists across a relaunch.
//
// TWO canvas families, each needing a DIFFERENT lever:
//
//   1. ScreenSpaceOverlay + ScaleWithScreenSize   ("Canvas", "Canvas in front")
//        The HUD. Lever = matchWidthOrHeight + referenceResolution.
//        KEEP MATCH AT 1.0 — lowering it re-introduces width-based scaling and
//        undoes the shrink (match .8 + ref 1350 => 1.281, vs match 1 => 1.067).
//
//   2. WorldSpace canvases                        ("NamePlate", etc.)
//        In-world labels. CanvasScaler.scaleFactor DOES NOT WORK here: for a
//        WorldSpace canvas, CanvasScaler.HandleWorldCanvas() overwrites
//        scaleFactor with dynamicPixelsPerUnit every frame. The real lever is
//        the canvas transform's localScale.
//
// NOTE: Canvas.scaleFactor is recomputed in CanvasScaler.Update(), so reading it
// right after writing returns LAST frame's value. We print the mathematically
// expected scale instead, and echo BEFORE state (which is accurate) each run.

// ---- KNOBS -----------------------------------------------------------------
float MATCH        = 1.0f;   // HUD. Leave at 1.0.
float HUD_SCALE    = 0.85f;  // HUD. <1 = smaller HUD + more layout room.
float WORLD_SCALE  = 0f;     // In-world labels. 0 = don't touch.
                             // Absolute multiplier vs the BASELINE printed below.
                             // Run once with 0 to read baseline, then e.g. 0.8f.
bool  LIST_ONLY    = false;  // true = report only, change nothing
// ----------------------------------------------------------------------------

var log = new System.Text.StringBuilder();
log.AppendLine($"KNOBS match={MATCH} hudScale={HUD_SCALE} worldScale={WORLD_SCALE} listOnly={LIST_ONLY}");
log.AppendLine($"SCREEN {UnityEngine.Screen.width}x{UnityEngine.Screen.height} " +
    $"aspect={(float)UnityEngine.Screen.width / UnityEngine.Screen.height:F4}");

foreach (var cs in UnityEngine.Object.FindObjectsOfType<UnityEngine.UI.CanvasScaler>())
{
    var cv = cs.GetComponent<UnityEngine.Canvas>();
    if (cv == null) continue;

    if (cv.renderMode == UnityEngine.RenderMode.WorldSpace)
    {
        var ls = cv.transform.localScale;
        log.AppendLine($"WORLD  {cs.name}: localScale={ls} (dynPPU={cs.dynamicPixelsPerUnit})");
        if (!LIST_ONLY && WORLD_SCALE > 0f)
        {
            // Store the pristine scale on first touch so repeated runs don't compound.
            var marker = cv.transform.Find("__uwp_baseline");
            float bx;
            if (marker == null)
            {
                var go = new UnityEngine.GameObject("__uwp_baseline");
                go.transform.SetParent(cv.transform, false);
                go.transform.localScale = ls;      // stash baseline here
                bx = ls.x;
            }
            else bx = marker.localScale.x;

            cv.transform.localScale = new UnityEngine.Vector3(bx, bx, bx) * WORLD_SCALE;
            log.AppendLine($"       APPLIED localScale baseline={bx:F5} -> {cv.transform.localScale}");
        }
        continue;
    }

    log.AppendLine($"BEFORE {cs.name} [{cs.uiScaleMode}/{cv.renderMode}] ref={cs.referenceResolution} " +
        $"match={cs.matchWidthOrHeight}");

    if (!LIST_ONLY && cs.uiScaleMode == UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize)
    {
        cs.matchWidthOrHeight = MATCH;
        if (HUD_SCALE > 0f && System.Math.Abs(HUD_SCALE - 1f) > 0.0001f)
            cs.referenceResolution = new UnityEngine.Vector2(cs.referenceResolution.x, 1080f / HUD_SCALE);

        // expected scale (accurate NOW; Canvas.scaleFactor lags by a frame)
        var r = cs.referenceResolution;
        double exp = System.Math.Pow(UnityEngine.Screen.width / r.x, 1.0 - MATCH) *
                     System.Math.Pow(UnityEngine.Screen.height / r.y, MATCH);
        log.AppendLine($"       APPLIED ref={r} match={MATCH} => expectedScale={exp:F4} " +
            $"logicalCanvas={UnityEngine.Screen.width / exp:F0}x{UnityEngine.Screen.height / exp:F0}");
    }
}

UnityExplorer.ExplorerCore.Log(log.ToString());
