<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ModTools.UI;
using ModTools.Utils;
using UnityEngine;

namespace ModTools.Explorer
{
    internal sealed class SceneExplorer : GUIWindow, IGameObject, IAwakingObject
    {
        private static readonly Queue<ReferenceChain> ShowRequests = new Queue<ReferenceChain>();
        
        private const float WindowTopMargin = 16.0f;
        private const float WindowBottomMargin = 8.0f;

        private const float HeaderHeightCompact = 1.65f;
        private const float HeaderHeightExpanded = 13.75f;

        private const float SceneTreeWidth = 320.0f;

        private const string ExpandDownButtonText = " ▼ ▼ ▼ ";
        private const string CollapseUpButtonText = " ▲ ▲ ▲ ";
        private const string ExpandRightButtonText = " ▶▶▶ ";
        private const string CollapseLeftButtonText = " ◀◀◀ ";

        private readonly GUIArea headerArea;
        private readonly GUIArea sceneTreeArea;
        private readonly GUIArea componentArea;

        private Dictionary<GameObject, bool> sceneRoots = new Dictionary<GameObject, bool>();

        private string findGameObjectFilter = string.Empty;
        private string findObjectTypeFilter = string.Empty;
        private string searchDisplayString = string.Empty;

        private Vector2 sceneTreeScrollPosition = Vector2.zero;
        private Vector2 componentScrollPosition = Vector2.zero;
        private SceneExplorerState state;

        private bool headerExpanded;
        private bool treeExpanded = true;

        public SceneExplorer()
            : base("Scene Explorer", new Rect(128, 440, 800, 500), Skin)
        {

            
            headerArea = new GUIArea(this)
                .ChangeSizeRelative(height: 0)
                .OffsetBy(vertical: WindowTopMargin);

            sceneTreeArea = new GUIArea(this)
                .ChangeSizeRelative(width: 0)
                .ChangeSizeBy(width: SceneTreeWidth);

            componentArea = new GUIArea(this)
                .OffsetBy(horizontal: SceneTreeWidth)
                .ChangeSizeBy(width: -SceneTreeWidth);

            state = new SceneExplorerState();

            RecalculateAreas();
        }

        public void Awake() => Plopper.Reset();

        public void Update() => Plopper.Update();

        public void RecalculateAreas()
        {
            if (!treeExpanded || WindowRect.width < Screen.width / 4)
            {
                sceneTreeArea.ChangeSizeBy(width: 0);

                componentArea
                    .OffsetBy(horizontal: 0)
                    .ChangeSizeBy(width: 0f);
            }
            else
            {
                sceneTreeArea.ChangeSizeBy(width: SceneTreeWidth);

                componentArea
                    .OffsetBy(horizontal: SceneTreeWidth)
                    .ChangeSizeBy(width: -SceneTreeWidth);
            }

            var headerHeight = headerExpanded ? HeaderHeightExpanded : HeaderHeightCompact;
            headerHeight *= MainWindow.Instance.Config.FontSize;
            headerHeight += 32.0f;

            var verticalOffset = headerHeight - WindowTopMargin;
            var verticalSizeOffset = -(verticalOffset + WindowBottomMargin);

            headerArea.ChangeSizeBy(height: verticalOffset);

            sceneTreeArea
                .OffsetBy(vertical: verticalOffset)
                .ChangeSizeBy(height: verticalSizeOffset);

            componentArea
                .OffsetBy(vertical: verticalOffset)
                .ChangeSizeBy(height: verticalSizeOffset);
        }

        public void Refresh()
        {
            sceneRoots = GameObjectUtil.FindSceneRoots();
            TypeUtil.ClearTypeCache();
        }
        
        

        public void Show(ReferenceChain refChain)
        {
            if (refChain == null)
            {
                Logger.Error("SceneExplorer: Show(): Null refChain");
                return;
            }
            ShowRequests.Enqueue(refChain);
            Visible = true;
        }        
         
        private void ProcessShowRequest(ReferenceChain refChain)
        {
            if (refChain == null)
            {
                Logger.Error("SceneExplorer: ProcessShowRequest(): Null refChain");
                return;
            }

            if (refChain.Length == 0)
            {
                Logger.Error("SceneExplorer: ExpandFromRefChain(): Invalid refChain, expected Length >= 0");
                return;
            }

            if (refChain.FirstItemType != ReferenceChain.ReferenceType.GameObject)
            {
                Logger.Error($"SceneExplorer: ExpandFromRefChain(): invalid chain type for element [0] - expected {ReferenceChain.ReferenceType.GameObject}, got {refChain.FirstItemType}");
                return;
            }

            sceneRoots.Clear();
            ClearExpanded();
            var typeName = refChain.Evaluate()?.GetType().Name; 
            searchDisplayString = $"Showing {typeName} \"{refChain}\"";

            var rootGameObject = (GameObject)refChain.GetChainItem(0);
            sceneRoots.Add(rootGameObject, true);

            var expandedRefChain = new ReferenceChain().Add(rootGameObject);
            state.ExpandedGameObjects.Add(expandedRefChain.UniqueId);

            for (var i = 1; i < refChain.Length; i++)
            {
                switch (refChain.GetChainItemType(i))
                {
                    case ReferenceChain.ReferenceType.GameObject:
                        var go = (GameObject)refChain.GetChainItem(i);
                        expandedRefChain = expandedRefChain.Add(go);
                        state.ExpandedGameObjects.Add(expandedRefChain.UniqueId);
                        break;

                    case ReferenceChain.ReferenceType.Component:
                        var component = (Component)refChain.GetChainItem(i);
                        expandedRefChain = expandedRefChain.Add(component);
                        state.ExpandedComponents.Add(expandedRefChain.UniqueId);
                        break;

                    case ReferenceChain.ReferenceType.Field:
                        var field = (FieldInfo)refChain.GetChainItem(i);
                        expandedRefChain = expandedRefChain.Add(field);
                        state.ExpandedObjects.Add(expandedRefChain.UniqueId);
                        break;

                    case ReferenceChain.ReferenceType.Property:<commit_msg>Change SceneExplorer title according to the currently displayed reference chain<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ModTools.UI;
using ModTools.Utils;
using UnityEngine;

namespace ModTools.Explorer
{
    internal sealed class SceneExplorer : GUIWindow, IGameObject, IAwakingObject
    {
        private static readonly Queue<ReferenceChain> ShowRequests = new Queue<ReferenceChain>();
        
        private const float WindowTopMargin = 16.0f;
        private const float WindowBottomMargin = 8.0f;

        private const float HeaderHeightCompact = 1.65f;
        private const float HeaderHeightExpanded = 13.75f;

        private const float SceneTreeWidth = 320.0f;

        private const string TitleBase = "Scene Explorer";
        private const string ExpandDownButtonText = " ▼ ▼ ▼ ";
        private const string CollapseUpButtonText = " ▲ ▲ ▲ ";
        private const string ExpandRightButtonText = " ▶▶▶ ";
        private const string CollapseLeftButtonText = " ◀◀◀ ";

        private readonly GUIArea headerArea;
        private readonly GUIArea sceneTreeArea;
        private readonly GUIArea componentArea;

        private Dictionary<GameObject, bool> sceneRoots = new Dictionary<GameObject, bool>();

        private string findGameObjectFilter = string.Empty;
        private string findObjectTypeFilter = string.Empty;
        private string searchDisplayString = string.Empty;

        private Vector2 sceneTreeScrollPosition = Vector2.zero;
        private Vector2 componentScrollPosition = Vector2.zero;
        private SceneExplorerState state;

        private bool headerExpanded;
        private bool treeExpanded = true;

        public SceneExplorer()
            : base(TitleBase, new Rect(128, 440, 800, 500), Skin)
        {

            
            headerArea = new GUIArea(this)
                .ChangeSizeRelative(height: 0)
                .OffsetBy(vertical: WindowTopMargin);

            sceneTreeArea = new GUIArea(this)
                .ChangeSizeRelative(width: 0)
                .ChangeSizeBy(width: SceneTreeWidth);

            componentArea = new GUIArea(this)
                .OffsetBy(horizontal: SceneTreeWidth)
                .ChangeSizeBy(width: -SceneTreeWidth);

            state = new SceneExplorerState();

            RecalculateAreas();
        }

        public void Awake() => Plopper.Reset();

        public void Update()
        {
            var refChain = state.CurrentRefChain;
            if (refChain != null && refChain.Length > 0)
            {
                var typeName = refChain.Evaluate().GetType().Name;
                Title = $"{TitleBase} - {typeName} \"{refChain}\"";
            }
            else
            {
                Title = TitleBase;
            }

            Plopper.Update();
        }

        public void RecalculateAreas()
        {
            if (!treeExpanded || WindowRect.width < Screen.width / 4)
            {
                sceneTreeArea.ChangeSizeBy(width: 0);

                componentArea
                    .OffsetBy(horizontal: 0)
                    .ChangeSizeBy(width: 0f);
            }
            else
            {
                sceneTreeArea.ChangeSizeBy(width: SceneTreeWidth);

                componentArea
                    .OffsetBy(horizontal: SceneTreeWidth)
                    .ChangeSizeBy(width: -SceneTreeWidth);
            }

            var headerHeight = headerExpanded ? HeaderHeightExpanded : HeaderHeightCompact;
            headerHeight *= MainWindow.Instance.Config.FontSize;
            headerHeight += 32.0f;

            var verticalOffset = headerHeight - WindowTopMargin;
            var verticalSizeOffset = -(verticalOffset + WindowBottomMargin);

            headerArea.ChangeSizeBy(height: verticalOffset);

            sceneTreeArea
                .OffsetBy(vertical: verticalOffset)
                .ChangeSizeBy(height: verticalSizeOffset);

            componentArea
                .OffsetBy(vertical: verticalOffset)
                .ChangeSizeBy(height: verticalSizeOffset);
        }

        public void Refresh()
        {
            sceneRoots = GameObjectUtil.FindSceneRoots();
            TypeUtil.ClearTypeCache();
        }
        
        

        public void Show(ReferenceChain refChain)
        {
            if (refChain == null)
            {
                Logger.Error("SceneExplorer: Show(): Null refChain");
                return;
            }
            ShowRequests.Enqueue(refChain);
            Visible = true;
        }        
         
        private void ProcessShowRequest(ReferenceChain refChain)
        {
            if (refChain == null)
            {
                Logger.Error("SceneExplorer: ProcessShowRequest(): Null refChain");
                return;
            }

            if (refChain.Length == 0)
            {
                Logger.Error("SceneExplorer: ExpandFromRefChain(): Invalid refChain, expected Length >= 0");
                return;
            }

            if (refChain.FirstItemType != ReferenceChain.ReferenceType.GameObject)
            {
                Logger.Error($"SceneExplorer: ExpandFromRefChain(): invalid chain type for element [0] - expected {ReferenceChain.ReferenceType.GameObject}, got {refChain.FirstItemType}");
                return;
            }

            sceneRoots.Clear();
            ClearExpanded();
            searchDisplayString = string.Empty;

            var rootGameObject = (GameObject)refChain.GetChainItem(0);
            sceneRoots.Add(rootGameObject, true);

            var expandedRefChain = new ReferenceChain().Add(rootGameObject);
            state.ExpandedGameObjects.Add(expandedRefChain.UniqueId);

            for (var i = 1; i < refChain.Length; i++)
            {
                switch (refChain.GetChainItemType(i))
                {
                    case ReferenceChain.ReferenceType.GameObject:
                        var go = (GameObject)refChain.GetChainItem(i);
                        expandedRefChain = expandedRefChain.Add(go);
                        state.ExpandedGameObjects.Add(expandedRefChain.UniqueId);
                        break;

                    case ReferenceChain.ReferenceType.Component:
                        var component = (Component)refChain.GetChainItem(i);
                        expandedRefChain = expandedRefChain.Add(component);
                        state.ExpandedComponents.Add(expandedRefChain.UniqueId);
                        break;

                    case ReferenceChain.ReferenceType.Field:
                        var field = (FieldInfo)refChain.GetChainItem(i);
                        expandedRefChain = expandedRefChain.Add(field);
                        state.ExpandedObjects.Add(expandedRefChain.UniqueId);
                        break;

                    case ReferenceChain.ReferenceType.Property:
{'fraction_non_alphanumeric': 0.0800237547323881, 'fraction_numerical': 0.006903719100289511, 'mean_word_length': 1.7840462905559, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6181448', 'n_tokens_mistral': 3433, 'n_tokens_neox': 3172, 'n_words': 706}