using System;
using UnityEditor;
using UnityEngine;
using System.Threading.Tasks;
using System.Collections.Generic;
// All using statements now refer to the new data-only class
using static IntelligentProjectAnalyzer.Editor.DependencyBuilderData;
namespace IntelligentProjectAnalyzer.Editor
{
///
/// Acts as the central state machine and orchestrator of the dependency build process.
/// This version performs the scan asynchronously and is driven by editor events.
///
public static class DependencyStoreBuilder
{
private const int BatchSize = 20; // Number of assets to process per frame.
#region State Management
private enum BuilderState { Idle, PreFetching, Analyzing, Finalizing }
private static BuilderState _currentState = BuilderState.Idle;
// Queues for assets to be processed on the main thread
private static Queue _scriptGuidsToProcess;
private static Queue _prefabGuidsToProcess;
private static Queue _soGuidsToProcess;
private static Queue _sceneGuidsToProcess;
private static Queue _miscGuidsToProcess;
// Lists to store the pre-fetched metadata
private static List _allMetadata;
private static List _scriptMetadata;
private static List _sceneMetadata;
private static List _miscMetadata;
// Task for background analysis
private static Task> _analysisTask;
#endregion
// Static constructor to hook into the editor update loop.
static DependencyStoreBuilder()
{
EditorApplication.update += OnEditorUpdate;
}
///
/// Starts the build process. Called by DependencyBuilderTriggers.
///
public static void BuildStore()
{
if (_currentState != BuilderState.Idle)
{
// A build is already running.
return;
}
// --- Initialize State and Queues ---
Debug.Log($"--- Starting Async Dependency Store Build ---");
DependencyCacheManager.CleanAndPrepareCache();
(_scriptGuidsToProcess, _prefabGuidsToProcess, _soGuidsToProcess, _sceneGuidsToProcess, _miscGuidsToProcess) = AssetDataFetcher.FindAssetGuids();
var analyzerSettings = AnalyzerSettingsCrud.GetOrCreateSettings();
if (!analyzerSettings.AnalyzeScripts)
{
_scriptGuidsToProcess?.Clear();
}
if (!analyzerSettings.AnalyzePrefabs)
{
_prefabGuidsToProcess?.Clear();
}
if (!analyzerSettings.AnalyzeScriptableObjects)
{
_soGuidsToProcess?.Clear();
}
if (!analyzerSettings.AnalyzeMiscellaneous)
{
_miscGuidsToProcess?.Clear();
}
_allMetadata = new List();
_scriptMetadata = new List();
_sceneMetadata = new List();
_miscMetadata = new List();
_currentState = BuilderState.PreFetching;
}
private static void OnEditorUpdate()
{
// The state machine is processed here, once per editor frame.
if (_currentState == BuilderState.Idle) return;
try
{
switch (_currentState)
{
case BuilderState.PreFetching:
ProcessPreFetchingBatch();
break;
case BuilderState.Analyzing:
WaitForAnalysis();
break;
case BuilderState.Finalizing:
FinalizeBuild();
break;
}
}
catch (Exception e)
{
Debug.LogError($"An error occurred during the build process. Aborting. Exception: {e}");
ResetState();
}
}
private static void ProcessPreFetchingBatch()
{
var totalAssets = _scriptGuidsToProcess.Count + _prefabGuidsToProcess.Count + _soGuidsToProcess.Count + _sceneGuidsToProcess.Count + _miscGuidsToProcess.Count;
var initialTotal = _allMetadata.Count + totalAssets;
for (var i = 0; i < BatchSize; i++)
{
if (_sceneGuidsToProcess.Count > 0)
{
var meta = AssetDataFetcher.PreFetchSceneMetadata(_sceneGuidsToProcess.Dequeue());
if (meta != null)
{
_allMetadata.Add(meta);
_sceneMetadata.Add(meta);
}
}
else if (_scriptGuidsToProcess.Count > 0)
{
var meta = AssetDataFetcher.PreFetchScriptMetadata(_scriptGuidsToProcess.Dequeue());
if (meta != null)
{
_allMetadata.Add(meta);
_scriptMetadata.Add(meta);
}
}
else if (_prefabGuidsToProcess.Count > 0)
{
var meta = AssetDataFetcher.PreFetchPrefabMetadata(_prefabGuidsToProcess.Dequeue());
if (meta != null) _allMetadata.Add(meta);
}
else if (_soGuidsToProcess.Count > 0)
{
var meta = AssetDataFetcher.PreFetchScriptableObjectMetadata(_soGuidsToProcess.Dequeue());
if (meta != null) _allMetadata.Add(meta);
}
else if (_miscGuidsToProcess.Count > 0)
{
var meta = AssetDataFetcher.PreFetchMiscAssetMetadata(_miscGuidsToProcess.Dequeue());
if (meta != null)
{
_allMetadata.Add(meta);
_miscMetadata.Add(meta);
}
}
else
{
// All pre-fetching is complete.
Debug.Log("Pre-fetching complete. Starting background analysis...");
StartBackgroundAnalysis();
_currentState = BuilderState.Analyzing;
return;
}
}
// Update progress bar
var progress = initialTotal > 0 ? (float)_allMetadata.Count / initialTotal : 1;
EditorUtility.DisplayProgressBar("Building Dependency Store (1/3)", "Pre-fetching asset data...", progress);
}
private static void StartBackgroundAnalysis()
{
// Gather all the data needed for the background task.
var roslynSetupData = AssetDataFetcher.PreFetchRoslynSetupData(_scriptMetadata);
// Start the background task, passing it the data it needs.
_analysisTask = Task.Run(() => BackgroundAnalyzer.RunAnalysis(_allMetadata, roslynSetupData));
}
private static void WaitForAnalysis()
{
if (_analysisTask == null)
{
Debug.LogError("Analysis task was not started correctly. Aborting.");
ResetState();
return;
}
EditorUtility.DisplayProgressBar("Building Dependency Store (2/3)", "Analyzing dependencies in background...", _analysisTask.IsCompleted ? 1f : 0.5f);
if (_analysisTask.IsCompleted)
{
_currentState = BuilderState.Finalizing;
}
}
private static void FinalizeBuild()
{
EditorUtility.DisplayProgressBar("Building Dependency Store (3/3)", "Writing results to disk...", 0.9f);
if (_analysisTask.IsFaulted)
{
Debug.LogError($"Background analysis failed: {_analysisTask.Exception}");
}
else
{
var results = _analysisTask.Result;
DependencyCacheManager.WriteResults(results);
Debug.Log("--- Async Dependency Store Build Complete! ---");
}
ResetState();
}
private static void ResetState()
{
EditorUtility.ClearProgressBar();
_currentState = BuilderState.Idle;
_analysisTask = null;
}
}
}