using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using ParadoxNotion;
using UnityEngine;
namespace NodeCanvas.Framework{
///Marks the BBParameter possible to only pick values from a blackboard
[AttributeUsage(AttributeTargets.Field)]
public class BlackboardOnlyAttribute : Attribute{}
///Class for Parameter Variables that allow binding to a Blackboard variable or specifying a value directly.
[Serializable]
[ParadoxNotion.Design.SpoofAOT]
abstract public class BBParameter {
[SerializeField]
private string _name; //null means use local _value, empty means |NONE|, anything else means use bb variable.
[SerializeField]
private string _targetVariableID;
[NonSerialized]
private IBlackboard _bb;
[NonSerialized]
private Variable _varRef;
//required
public BBParameter(){}
///Create and return an instance of a generic BBParameter<T> with type argument provided and set to read from the specified blackboard
public static BBParameter CreateInstance(Type t, IBlackboard bb){
if (t == null) return null;
var newBBParam = (BBParameter)Activator.CreateInstance( typeof(BBParameter<>).RTMakeGenericType(new Type[]{t}) );
newBBParam.bb = bb;
return newBBParam;
}
///Set the blackboard reference provided for all BBParameters and List<BBParameter> fields on the target object provided.
public static void SetBBFields(object o, IBlackboard bb){
var bbParams = GetObjectBBParameters(o);
for (var i = 0; i < bbParams.Count; i++){
bbParams[i].bb = bb;
}
}
///Returns BBParameters found in target object
public static List<BBParameter> GetObjectBBParameters(object o){
var bbParams = new List<BBParameter>();
var fields = o.GetType().RTGetFields();
for (var i = 0; i < fields.Length; i++){
var field = fields[i];
if (field.FieldType.RTIsSubclassOf(typeof(BBParameter))){
var value = field.GetValue(o);
if (value == null){
value = Activator.CreateInstance(field.FieldType);
field.SetValue(o, value);
}
bbParams.Add( (BBParameter)value );
continue;
}
if (typeof(IList).RTIsAssignableFrom(field.FieldType) && !field.FieldType.IsArray && typeof(BBParameter).RTIsAssignableFrom(field.FieldType.RTGetGenericArguments()[0]) ){
var list = field.GetValue(o) as IList;
if (list != null){
for (var j = 0; j < list.Count; j++){
var bbParam = (BBParameter)list[j];
if (bbParam == null){
bbParam = (BBParameter)Activator.CreateInstance( field.FieldType.RTGetGenericArguments()[0] );
list[j] = bbParam;
}
bbParams.Add( bbParam );
}
}
continue;
}
if (o is ISubParametersContainer){
var parameters = (o as ISubParametersContainer).GetIncludeParseParameters();
if (parameters != null){
bbParams.AddRange( parameters );
}
}
}
return bbParams;
}
private Variable ResolveReference(IBlackboard targetBlackboard, bool useID){
var targetName = this.name;
if (targetName != null && targetName.Contains("/")){
var split = targetName.Split('/');
targetBlackboard = GlobalBlackboard.Find(split[0]);
targetName = split[1];
}
Variable result = null;
if (targetBlackboard == null){ return null; }
if (useID && targetVariableID != null){ result = targetBlackboard.GetVariableByID(targetVariableID); }
if (result == null && !string.IsNullOrEmpty(targetName)){ result = targetBlackboard.GetVariable(targetName, varType); }
return result;
}
///The target variable ID
private string targetVariableID{
get {return _targetVariableID;}
set {_targetVariableID = value;}
}
///The Variable object reference if any.One is set after a get or set as well as well when SetBBFields is called
///Setting the varRef also binds this parameter with that Variable.
public Variable varRef{
get {return _varRef;}
set
{
if (_varRef != value){
if (_varRef != null){
_varRef.onNameChanged -= UpdateName; //remove old one
}
if (value != null){
value.onNameChanged += UpdateName; //add new one
UpdateName(value.name); //update name immediately
}
_varRef = value;
targetVariableID = value != null? value.ID : null;
Bind(value);
}
}
}
void UpdateName(string newName){
if (_name.Contains("/")){ //is global
var bbName = _name.Split('/')[0];
newName = bbName + "/" + newName;
}
_name = newName;
}
///The blackboard to read/write from. Setting this also sets the variable reference if found
public IBlackboard bb{
get {return _bb;}
set
{
if (_bb != value){
_bb = value;
varRef = value != null? ResolveReference(_bb, true) : null;
}
}
}
///The name of the Variable to read/write from. Null if not, Empty if |NONE|.
public string name{
get { return _name; }
set
{
if (_name != value){
_name = value;
varRef = value != null? ResolveReference(bb, false) : null;
}
}
}
///Should the variable read from a blackboard variable?
public bool useBlackboard{
get { return name != null; }
set
{
if (value == false){
name = null;
}
if (value == true && name == null){
name = string.Empty;
}
}
}
///Has the user selected |NONE| in the dropdown?
public bool isNone{
get {return name == string.Empty;}
}
///Is the final value null?
public bool isNull{
get { return object.Equals(objectValue, null); }
}
///The type of the Variable reference or null if there is no Variable referenced. The returned type is for most cases the same as 'VarType'
public Type refType{
get {return varRef != null? varRef.varType : null;}
}
///The value as object type when accessing from base class. Don't do this regularely.
public object value{
get {return objectValue;}
set {objectValue = value;}
}
///The raw object value
abstract protected object objectValue{get;set;}
///The type of the value that this BBParameter holds
abstract public Type varType{get;}
///Bind the BBParameter to target. Null unbinds.
abstract protected void Bind(Variable data);
public override string ToString(){
if (isNone)
return "<b>NONE</b>";
if (useBlackboard)
return string.Format("<b>${0}</b>", name);
if (isNull)
return "<b>NULL</b>";
if (objectValue is IList)
return string.Format("<b>{0}</b>", varType.FriendlyName());
if (objectValue is IDictionary)
return string.Format("<b>{0}</b>", varType.FriendlyName());
return string.Format("<b>{0}</b>", objectValue.ToStringAdvanced() );
}
}
///Use BBParameter to create a parameter possible to be linked to a blackboard Variable
[Serializable]
public class BBParameter<T> : BBParameter{
public BBParameter() {}
public BBParameter(T value) { _value = value; }
//delegates for Variable binding
private Func<T> getter;
private Action<T> setter;
//
[SerializeField]
protected T _value;
new public T value{
get
{
if (getter != null){
return getter();
}
//Dynamic?
if (name != null && bb != null){
//setting the varRef property also binds it.
//this will not create a new var but get one if exists already.
varRef = bb.GetVariable(name, varType);
return getter != null? getter() : default(T);
}
return _value;
}
set
{
if (setter != null){
setter(value);
return;
}
if (isNone){
return;
}
//Dynamic?
if (name != null && bb != null){
//setting the varRef property also binds it
//this will create a new var if one does not exists
varRef = bb.SetValue(name, value);
return;
}
_value = value;
}
}
protected override object objectValue{
get {return value;}
set {this.value = (T)value;}
}
public override Type varType{
get {return typeof(T);}
}
///Binds this BBParameter to a Variable. Null unbinds
protected override void Bind(Variable data){
if (data == null){
getter = null;
setter = null;
_value = default(T);
return;
}
BindGetter(data);
BindSetter(data);
}
//Bind the Getter
bool BindGetter(Variable data){
if (data is Variable<T>){
getter = (data as Variable<T>).GetValue;
return true;
}
if (data.CanConvertTo(varType)){
var func = data.GetGetConverter(varType);
getter = ()=>{ return (T)func(); };
return true;
}
return false;
}
//Bind the Setter
bool BindSetter(Variable data){
if (data is Variable<T>){
setter = (data as Variable<T>).SetValue;
return true;
}
if (data.CanConvertFrom(varType)){
var func = data.GetSetConverter(varType);
setter = (T value)=>{ func(value); };
return true;
}
return false;
}
public static implicit operator BBParameter<T>(T value) {
return new BBParameter<T>{value = value};
}
/*
public static implicit operator T(BBParameter<T> param) {
return param.value;
}
*/
}
}