<commit_before>package flixel.util;

#if macro
import haxe.macro.Expr;
#else
import flixel.util.FlxDestroyUtil.IFlxDestroyable;

typedef FlxSignal = FlxTypedSignal<Void->Void>;

@:multiType
abstract FlxTypedSignal<T>(IFlxSignal<T>)
{
	public var dispatch(get, never):T;
	
	public function new();
	
	public inline function add(listener:T):Void 
	{
		this.add(listener);
	}
	
	public inline function addOnce(listener:T):Void 
	{
		this.addOnce(listener);
	}
	
	public inline function remove(listener:T):Void
	{
		this.remove(listener);
	}
	
	public inline function has(listener:T):Bool 
	{
		return this.has(listener);
	}
	
	public inline function removeAll():Void 
	{
		this.removeAll();
	}
	
	inline function get_dispatch():T
	{
		return this.dispatch;
	}
	
	@:to 
	static inline function toSignal0(signal:IFlxSignal<Void->Void>):FlxSignal0 
	{
		return new FlxSignal0();
	}
	
	@:to 
	static inline function toSignal1<T1>(signal:IFlxSignal<T1->Void>):FlxSignal1<T1> 
	{
		return new FlxSignal1();
	}
	
	@:to 
	static inline function toSignal2<T1, T2>(signal:IFlxSignal<T1->T2->Void>):FlxSignal2<T1, T2> 
	{
		return new FlxSignal2();
	}
	
	@:to 
	static inline function toSignal3<T1, T2, T3>(signal:IFlxSignal<T1->T2->T3->Void>):FlxSignal3<T1, T2, T3> 
	{
		return new FlxSignal3();
	}
	
	@:to 
	static inline function toSignal4<T1, T2, T3, T4>(signal:IFlxSignal<T1->T2->T3->T4->Void>):FlxSignal4<T1, T2, T3, T4> 
	{
		return new FlxSignal4();
	}
}

private class FlxSignalHandler<T> implements IFlxDestroyable
{
	public var listener:T;
	public var dispatchOnce(default, null):Bool = false;
	
	public function new(listener:T, dispatchOnce:Bool) 
	{
		this.listener = listener;
		this.dispatchOnce = dispatchOnce;
	}
	
	public function destroy()
	{
		listener = null;
	}
}

private class FlxBaseSignal<T> implements IFlxSignal<T> 
{
	/**
	 * Typed function reference used to dispatch this signal.
	 */
	public var dispatch:T;
	
	var handlers:Array<FlxSignalHandler<T>>;
	var pendingRemove:Array<FlxSignalHandler<T>>;
	var processingListeners:Bool = false;
	
	public function new() 
	{
		handlers = [];
		pendingRemove = [];
	}
	
	public function add(listener:T)
	{
		if (listener != null)
			registerListener(listener, false);
	}
	
	public function addOnce(listener:T):Void
	{
		if (listener != null)
			registerListener(listener, true);
	}
	
	public function remove(listener:T):Void
	{
		if (listener != null)
		{
			var handler = getHandler(listener);
			if (handler != null)
			{
				if (processingListeners)
					pendingRemove.push(handler);
				else
				{
					handlers.remove(handler);
					handler.destroy();
				}
			}
		}
		
	}
	
	public function has(listener:T):Bool
	{
		if (listener == null)
			return false;
		return getHandler(listener) != null;
	}
	
	public inline function removeAll():Void 
	{
		FlxDestroyUtil.destroyArray(handlers);
	}
	
	public function destroy():Void
	{
		removeAll();
		handlers = null;
		pendingRemove = null;
	}
	
	function registerListener(listener:T, dispatchOnce:Bool):FlxSignalHandler<T>
	{
		var handler = getHandler(listener);
		
		if (handler == null)
		{
			handler = new FlxSignalHandler<T>(listener, dispatchOnce);
			handlers.push(handler);
			return handler;
		}
		else
		{
			// If the listener was previously added, definitely don't add it again.
			// But throw an exception if their once values differ.
			if (handler.dispatchOnce != dispatchOnce)
				throw "You cannot addOnce() then add() the same listener without removing the relationship first.";
			else
				return handler;
		}
	}
	
	function getHandler(listener:T):FlxSignalHandler<T>
	{
		for (handler in handlers)
		{
			if (
				#if neko // simply comparing the functions doesn't do the trick on neko
					Reflect.compareMethods(handler.listener, listener)
				#else
					handler.listener == listener
				#end )
			{
				return handler; // Listener was already registered.<commit_msg>Fix unit tests for HL<commit_after>package flixel.util;

#if macro
import haxe.macro.Expr;
#else
import flixel.util.FlxDestroyUtil.IFlxDestroyable;

typedef FlxSignal = FlxTypedSignal<Void->Void>;

@:multiType
abstract FlxTypedSignal<T>(IFlxSignal<T>)
{
	public var dispatch(get, never):T;
	
	public function new();
	
	public inline function add(listener:T):Void 
	{
		this.add(listener);
	}
	
	public inline function addOnce(listener:T):Void 
	{
		this.addOnce(listener);
	}
	
	public inline function remove(listener:T):Void
	{
		this.remove(listener);
	}
	
	public inline function has(listener:T):Bool 
	{
		return this.has(listener);
	}
	
	public inline function removeAll():Void 
	{
		this.removeAll();
	}
	
	inline function get_dispatch():T
	{
		return this.dispatch;
	}
	
	@:to 
	static inline function toSignal0(signal:IFlxSignal<Void->Void>):FlxSignal0 
	{
		return new FlxSignal0();
	}
	
	@:to 
	static inline function toSignal1<T1>(signal:IFlxSignal<T1->Void>):FlxSignal1<T1> 
	{
		return new FlxSignal1();
	}
	
	@:to 
	static inline function toSignal2<T1, T2>(signal:IFlxSignal<T1->T2->Void>):FlxSignal2<T1, T2> 
	{
		return new FlxSignal2();
	}
	
	@:to 
	static inline function toSignal3<T1, T2, T3>(signal:IFlxSignal<T1->T2->T3->Void>):FlxSignal3<T1, T2, T3> 
	{
		return new FlxSignal3();
	}
	
	@:to 
	static inline function toSignal4<T1, T2, T3, T4>(signal:IFlxSignal<T1->T2->T3->T4->Void>):FlxSignal4<T1, T2, T3, T4> 
	{
		return new FlxSignal4();
	}
}

private class FlxSignalHandler<T> implements IFlxDestroyable
{
	public var listener:T;
	public var dispatchOnce(default, null):Bool = false;
	
	public function new(listener:T, dispatchOnce:Bool) 
	{
		this.listener = listener;
		this.dispatchOnce = dispatchOnce;
	}
	
	public function destroy()
	{
		listener = null;
	}
}

private class FlxBaseSignal<T> implements IFlxSignal<T> 
{
	/**
	 * Typed function reference used to dispatch this signal.
	 */
	public var dispatch:T;
	
	var handlers:Array<FlxSignalHandler<T>>;
	var pendingRemove:Array<FlxSignalHandler<T>>;
	var processingListeners:Bool = false;
	
	public function new() 
	{
		handlers = [];
		pendingRemove = [];
	}
	
	public function add(listener:T)
	{
		if (listener != null)
			registerListener(listener, false);
	}
	
	public function addOnce(listener:T):Void
	{
		if (listener != null)
			registerListener(listener, true);
	}
	
	public function remove(listener:T):Void
	{
		if (listener != null)
		{
			var handler = getHandler(listener);
			if (handler != null)
			{
				if (processingListeners)
					pendingRemove.push(handler);
				else
				{
					handlers.remove(handler);
					handler.destroy();
				}
			}
		}
		
	}
	
	public function has(listener:T):Bool
	{
		if (listener == null)
			return false;
		return getHandler(listener) != null;
	}
	
	public inline function removeAll():Void 
	{
		FlxDestroyUtil.destroyArray(handlers);
	}
	
	public function destroy():Void
	{
		removeAll();
		handlers = null;
		pendingRemove = null;
	}
	
	function registerListener(listener:T, dispatchOnce:Bool):FlxSignalHandler<T>
	{
		var handler = getHandler(listener);
		
		if (handler == null)
		{
			handler = new FlxSignalHandler<T>(listener, dispatchOnce);
			handlers.push(handler);
			return handler;
		}
		else
		{
			// If the listener was previously added, definitely don't add it again.
			// But throw an exception if their once values differ.
			if (handler.dispatchOnce != dispatchOnce)
				throw "You cannot addOnce() then add() the same listener without removing the relationship first.";
			else
				return handler;
		}
	}
	
	function getHandler(listener:T):FlxSignalHandler<T>
	{
		for (handler in handlers)
		{
			if (
				#if (neko || hl) // simply comparing the functions doesn't do the trick on these targets
					Reflect.compareMethods(handler.listener, listener)
				#else
					handler.listener == listener
				#end )
			{
				return handler; // Listener was already registered.
{'fraction_non_alphanumeric': 0.12066981976223955, 'fraction_numerical': 0.011504537901060975, 'mean_word_length': 3.9145728643216082, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 79, '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': '2065548', 'n_tokens_mistral': 3028, 'n_tokens_neox': 2656, 'n_words': 617}