// ReSharper disable ConvertToAutoPropertyWhenPossible
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using UnityEngine;
using Object = UnityEngine.Object;
[assembly: InternalsVisibleTo("iCare.VisitorDetection.Tests")]
namespace iCare.VisitorDetection
{
public sealed class VisitorDetector<TVisitor> : IVisitorDetector
{
readonly bool _cacheVisitors;
readonly ConditionData _conditionData;
readonly VisitorFindStrategy _visitorFinder;
internal VisitorDetector(VisitorFindStrategy visitorFindStrategy, bool cacheVisitors,
ConditionData conditionData)
{
_visitorFinder = visitorFindStrategy;
_conditionData = conditionData;
_cacheVisitors = cacheVisitors;
}
public List<TVisitor> Visitors { get; } = new();
public bool HasVisitors => Visitors.Count > 0;
#if DEBUG
internal bool LogsActive { get; set; }
#endif
public event Action<TVisitor> OnVisitorDetectedEvent;
public event Action<TVisitor> OnVisitorLeftEvent;
void AddVisitor(Collider other, TVisitor visitor)
{
if (Visitors.Contains(visitor))
throw new InvalidOperationException("Visitor already exists in the list");
OnVisitorDetectedEvent?.Invoke(visitor);
if (_cacheVisitors)
{
Visitors.Add(visitor);
Log($"{visitor.SetBold()} added to CacheList", visitor as Object);
}
Log($"{other.name.Highlight()} successfully detected".SetColor(Color.green), other);
}
void RemoveVisitor(Collider other, TVisitor visitor)
{
#if DEBUG
var otherName = other.name.Highlight();
#endif
if (_cacheVisitors && Visitors.Contains(visitor))
{
Visitors.Remove(visitor);
Log($"{visitor.SetBold()} removed from CacheList", visitor as Object);
}
OnVisitorLeftEvent?.Invoke(visitor);
Log($"{otherName} successfully left".SetColor(Color.green), other);
}
internal sealed class Condition<T>
{
readonly Func<T, bool> _condition;
#if DEBUG
internal readonly string FailureMessage;
#endif
internal Condition(Func<T, bool> condition, string failureMessage)
{
_condition = condition;
FailureMessage = failureMessage;
}
internal bool IsMet(T visitor)
{
return _condition(visitor);
}
}
internal sealed class ConditionData
{
internal readonly List<Condition<VisitorDetector<TVisitor>>> DetectorConditions;
internal readonly List<Condition<Collider>> Preconditions;
internal readonly List<Condition<TVisitor>> VisitorConditions;
internal ConditionData(List<Condition<VisitorDetector<TVisitor>>> detectorConditions,
List<Condition<Collider>> preconditions,
List<Condition<TVisitor>> visitorConditions)
{
DetectorConditions = detectorConditions;
Preconditions = preconditions;
VisitorConditions = visitorConditions;
}
public void AddVisitorCondition(Func<TVisitor, bool> visitorCondition, string errorMessage = null)
{
VisitorConditions.Add(new Condition<TVisitor>(visitorCondition, errorMessage));
}
public void AddDetectorCondition(Func<VisitorDetector<TVisitor>, bool> detectorCondition,
string errorMessage = null)
{
DetectorConditions.Add(new Condition<VisitorDetector<TVisitor>>(detectorCondition, errorMessage));
}
public void AddPrecondition(Func<Collider, bool> precondition, string errorMessage = null)
{
Preconditions.Add(new Condition<Collider>(precondition, errorMessage));
}
}
#region OnTrigger
void IVisitorDetector.OnTriggerEnter(Collider other)
{
if (ConditionsAreMet(other, out var visitor)) return;
AddVisitor(other, visitor);
}
void IVisitorDetector.OnTriggerExit(Collider other)
{
if (!_visitorFinder.TryGetVisitor<TVisitor>(other, out var visitor))
{
Log($"{other.name} does not have {typeof(TVisitor).Name.Highlight()}".SetColor(Color.yellow), other);
return;
}
RemoveVisitor(other, visitor);
}
#endregion
#region Conditions
bool PreConditionsMet(Collider other)
{
foreach (var precondition in _conditionData.Preconditions)
{
if (precondition.IsMet(other)) continue;
Log(precondition.FailureMessage, other);
return false;
}
return true;
}
bool VisitorConditionsMet(TVisitor visitor)
{
foreach (var visitorCondition in _conditionData.VisitorConditions)
{
if (visitorCondition.IsMet(visitor)) continue;
Log(visitorCondition.FailureMessage, visitor as Object);
return false;
}
return true;
}
bool DetectorConditionsMet()
{
foreach (var detectorCondition in _conditionData.DetectorConditions)
{
if (detectorCondition.IsMet(this)) continue;
Log(detectorCondition.FailureMessage, null);
return false;
}
return true;
}
bool ConditionsAreMet(Collider other, out TVisitor visitor)
{
#if DEBUG
var otherName = other.name.Highlight();
#endif
visitor = default;
if (!PreConditionsMet(other))
{
Log($"{otherName} does not meet preconditions".SetColor(Color.yellow), other);
return true;
}
if (!DetectorConditionsMet())
{
Log($"{otherName} does not meet detector conditions".SetColor(Color.yellow), other);
return true;
}
if (!_visitorFinder.TryGetVisitor(other, out visitor))
{
Log($"{otherName} does not have {typeof(TVisitor).Name.Highlight()}".SetColor(Color.yellow), other);
return true;
}
if (!VisitorConditionsMet(visitor))
{
Log($"{otherName} does not meet visitor conditions".SetColor(Color.yellow), other);
return true;
}
return false;
}
#endregion
// ReSharper disable Unity.PerformanceAnalysis
[Conditional("DEBUG")]
void Log([CanBeNull] object message, Object context)
{
//Null check for failure messages
if (LogsActive && message != null)
UnityEngine.Debug.Log(message, context);
}
}
}


Log($"{otherName} does not meet preconditions".SetColor(Color.yellow), other);
this log calls should be removed from build as i know, but it does not so im having errors because of that. Isnt conditional attribute for this, so i can safely remove method calls without extra #if debug on every line
