using System ;

using System.Collections.Generic ;

using System.Reflection ;

using System.Text.RegularExpressions ;

using UnityEngine ;

using UnityEditor ;

using UnityEngine.SceneManagement ;

namespace Helios . Otto

{

//add me to an Editor folder

public class Otto

{

/// <summary>

/// Loops through all public fields on the target mono behaviour and searches for a relevant asset in the asset database

/// Separate your search terms with an underscore, and being as specific as possible with your variable names helps Otto to find the right assets

/// Example: "Optimistic Unicorn.mp4" can be found with variable names "Optimistic", "Unicorn", or best of all: "OptimisticUnicorn"

/// </summary>

[ MenuItem ( "CONTEXT/MonoBehaviour/Auto Populate - All" ) ]

static void AutoPopulateAll ( MenuCommand command )

{

Debug . Log ( "[Otto]: Auto populating variables" ) ;

var type = command . context . GetType ( ) ;

Debug . Log ( $ "[Otto]: Doing a kickflip over the {type.Name}" ) ;

var fields = type . GetFields ( ) ;

var fieldsSet = 0 ;

for ( int i = 0 ; i < fields . Length ; i ++ )

{

var assetName = ParseName ( fields [ i ] . Name ) ;

if ( SearchAssets ( command . context , assetName, fields [ i ] ) )

{

fieldsSet ++;

continue ;

}

if ( SearchScene ( command . context , assetName, fields [ i ] ) ) fieldsSet ++;

}

Debug . Log ( $ "[Otto]: All done! I set {fieldsSet} fields" ) ;

}

[ MenuItem ( "CONTEXT/MonoBehaviour/Auto Populate - Assets" ) ]

static void AutoPopulateAssets ( MenuCommand command )

{

Debug . Log ( "[Otto]: Auto populating variables" ) ;

var type = command . context . GetType ( ) ;

Debug . Log ( $ "[Otto]: Doing a kickflip over the {type.Name}" ) ;

var fields = type . GetFields ( ) ;

var fieldsSet = 0 ;

for ( int i = 0 ; i < fields . Length ; i ++ )

{

var assetName = ParseName ( fields [ i ] . Name ) ;

if ( SearchAssets ( command . context , assetName, fields [ i ] ) ) fieldsSet ++;

}

Debug . Log ( $ "[Otto]: All done! I set {fieldsSet} fields" ) ;

}

[ MenuItem ( "CONTEXT/MonoBehaviour/Auto Populate - Scene" ) ]

static void AutoPopulateScene ( MenuCommand command )

{

Debug . Log ( "[Otto]: Auto populating variables" ) ;

var type = command . context . GetType ( ) ;

Debug . Log ( $ "[Otto]: Doing a kickflip over the {type.Name}" ) ;

var fields = type . GetFields ( ) ;

var fieldsSet = 0 ;

for ( int i = 0 ; i < fields . Length ; i ++ )

{

var assetName = ParseName ( fields [ i ] . Name ) ;

if ( SearchScene ( command . context , assetName, fields [ i ] ) ) fieldsSet ++;

}

Debug . Log ( $ "[Otto]: All done! I set {fieldsSet} fields" ) ;

}

static bool SearchAssets ( UnityEngine . Object target, string search, FieldInfo field )

{

var asset = AssetDatabase . FindAssets ( search ) ;

if ( asset . Length > 0 )

{

var assetPath = AssetDatabase . GUIDToAssetPath ( asset [ 0 ] ) ;

var loadedAsset = AssetDatabase . LoadAssetAtPath ( assetPath, field . FieldType ) ;

if ( loadedAsset != null )

{

Undo . RecordObject ( target, $ "Set field {field.Name}" ) ;

field . SetValue ( target, loadedAsset ) ;

return true ;

}

}

return false ;

}

static bool SearchScene ( UnityEngine . Object target, string search, FieldInfo field )

{

//if we can't even find the obejct type in the scene, why search for it?

//i.e. you won't find a float or audio clip in the scene!

if ( TypeNotSceneCompatible ( field . FieldType ) ) return false ;

List < GameObject > obj = new List < GameObject > ( ) ;

SceneManager . GetActiveScene ( ) . GetRootGameObjects ( obj ) ;

var rootObj = new List < GameObject > ( obj ) ;

foreach ( GameObject o in rootObj )

{

for ( int childIndex = 0 ; childIndex < o . transform . childCount ; childIndex ++ )

{

var t = o . transform . GetChild ( childIndex ) ;

if ( ! obj . Contains ( t . gameObject ) ) obj . Add ( t . gameObject ) ;

}

}

object targetObject = obj . Find ( x => x . name . Contains ( search ) ) ;

if ( ! field . FieldType . Name . Contains ( "GameObject" ) )

{

targetObject = ( ( GameObject ) targetObject ) ?. GetComponent ( field . FieldType ) ;

}

if ( targetObject != null )

{

Undo . RecordObject ( target, $ "Set field {field.Name}" ) ;

field . SetValue ( target, targetObject ) ;

return true ;

}

return false ;

}

static string ParseName ( string name )

{

var parsedName = String . Join ( " " , Regex . Split ( name, @"(?<!^)(?=[A-Z])" ) ) ;

return parsedName . Replace ( '_' , ' ' ) ;

}

static bool TypeNotSceneCompatible ( Type type )

{

return ( ! type . IsSubclassOf ( typeof ( MonoBehaviour ) ) && ! type . IsSubclassOf ( typeof ( Behaviour ) ) && type . Name != "GameObject" ) ;

}

}