# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2022-2024, by Samuel Williams.

require 'fiber'

# @namespace
class Fiber
	# Provides compatibility shims for fiber storage.
	module Storage
		# Initialize the fiber with the given storage.
		def initialize(*arguments, storage: true, **options, &block)
			case storage
			when true
				@storage = Fiber.current.storage
			else
				raise TypeError, "Storage must be a hash!" unless storage.is_a?(Hash)
				
				@storage = storage
			end
			
			super(*arguments, **options, &block)
		end

		# Set the storage associated with this fiber, clearing any previous storage.
		def storage=(hash)
			@storage = hash.dup
		end
		
		# The storage associated with this fiber.
		def storage
			@storage.dup
		end
		
		# @private
		def __storage__
			@storage ||= {}
		end
	end
	
	unless Fiber.current.respond_to?(:storage)
		prepend Storage
		
		# Get a value from the current fiber's storage.
		def self.[] key
			raise TypeError, "Key must be symbol!" unless key.is_a?(Symbol)
			
			self.current.__storage__[key]
		end
		
		# Set a value in the current fiber's storage.
		def self.[]= key, value
			raise TypeError, "Key must be symbol!" unless key.is_a?(Symbol)
			
			self.current.__storage__[key] = value
		end
	else
		# Whether the fiber storage has buggy keys. Unfortunately the original implementation of fiber storage was broken, this method detects the bug and is used to apply a fix.
		def self.__borked_keys__
			!Fiber.new do
				key = :"#{self.object_id}.key"
				Fiber[key] = true
				Fiber[key]
			end.resume
		end
		
		if __borked_keys__
			# This is a fix for the original implementation of fiber storage which incorrectly handled non-dynamic symbol keys.
			module FixBorkedKeys
				# Lookup the value for the key, ensuring the symbol is dynamic.
				def [](key)
					raise TypeError, "Key must be symbol!" unless key.is_a?(Symbol)
					
					super(eval(key.inspect))
				end
				
				# Assign the value to the key, ensuring the symbol is dynamic.
				def []=(key, value)
					raise TypeError, "Key must be symbol!" unless key.is_a?(Symbol)
					
					super(eval(key.inspect), value)
				end
			end
			
			singleton_class.prepend FixBorkedKeys
		end
	end
end
