do ()->
	import parts/closure-helpers.coffee
	import parts/closure-errorsAndWarnings.coffee
	boundInstances = {}
	globalOptions = 
		silent:					false
		liveProps:				true
		dispatchEvents:			false
		updateEvenIfSame:		false
		updateOnBind:			true
		mutateInherited:		false
		trackArrayChildren:		false
		simpleSelector:			false
		promiseTransforms:		false
		placeholder:			['{{', '}}']

	setPholderRegEx()





	SimplyBind = (subject, options, isProxiedFunc)->
		if (!subject and subject isnt 0) or (!checkIf.isString(subject) and !checkIf.isNumber(subject) and !checkIf.isFunction(subject) and subject not instanceof Array)
			throwError('invalidParamName') unless checkIf.isBindingInterface(subject)

		if checkIf.isObject(subject) and subject not instanceof Array # Indicates it's a Binding instance object due to the above check
			return new BindingInterface subject._, 1#, subject.placeholder
		
		else return new BindingInterface null, 0, null, subject, isProxiedFunc, options









	Binding = (object, type, state, isProxiedFunc)->
		extendState(@, state)
		@type = type							# ObjectProp | Array | Func | Event | DOMAttr | DOMValue | DOMCheckbox | DOMRadio
		@object = object 						# The subject object of this binding, i.e. function, array, {}, DOM el, etc.
		@ID = genID() 							# Assigned only after passing a valid object to .of()
		@deps = []								# Dependents array listing all of the objects that will be updated upon value update
		@depsMap = {1:genObj(), 2:genObj()}		# Map dependents by ID on the '1' object if is one-way binding and '2' as wel if is two-way (deps that have this object as a dep as well)
		@depsPholders = genObj()				# DependentID:dep-placeholder mapping
		@myPholders = genObj() 					# DependentID:self-placeholder mapping indicating which deps will receive a value of a specific placeholder from this binding (if applicable)
		@transforms = genObj()					# DependentID:transformFn mapping
		@conditions = genObj() 					# DependentID:coniditionFn mapping
		@attachedEvents = []					# Array listing all of the events currently listened on @object


		# ==== Properties declared later or inherited from binding interface =================================================================================
		# @options = options
		# @valueOriginal = undefined			# Value on init
		# @value = undefined 					# Will represent the actual current value of the binding/object
		# @property = property					# The property name or array index or event callback argument
		# @selector = selector					# The property name or array index or event callback argument
		# @customEventMethod = {}				# Names of the event emitter/trigger methods (if applicable)
		# @pholderValues = {}					# Placeholder value mapping
		# @pholderContexts = {}					# Placeholder surroundings (original binding value split by the placeholder regEx)
		# @pholderIndexMap = {}					# Placeholder occurence mapping, i.e. the placeholder name for each placeholder occurence
		# @placeholder = ""						# The last specified placeholder to bind the value to
		# @descriptor = ""						# Describes the type of property, i.e. 'attr:data-name' to indicate a DOMAttr type binding
		# @isLiveProp = Boolean					# Indicates whether or not the Object/Object's propety have been modified to be a live property
		# @isDom = Boolean						# Indicates whether or not the binding's object is a DOM object
		# @pollInterval = ID					# The interval ID of the timer that manually polls the object's value at a set interval
		# @arrayBinding = Binding					# Reference to the parent array binding (if exists) for an index-of-array binding (i.e. SimplyBind(array))
		# @trackedChildren = []					# For Array type bindings - indicates which indicies of the array are tracked for changes (applicable when @options.trackArrayChildren) is true
		# @eventName = ""						# The name of the event this binding is listening to (for Event type bindings)
		# @isEmitter = Boolean 					# Tracker to let us know we shouldn't handle the event update we received as it is the event this binding just emitted
		# @eventHandler = Function 				# The callback that gets triggered upon an event emittance (for Even type bindings)
		# @eventObject = Event 					# The dispatched event object (for Event type bindings)
		# @selfTransform = Function 			# The transform function that new values being set to this binding are being passed through during @setValue (if applicable)
		# @throttleRate = milliseconds			# The rate in which the binding's dependents will be updated only once in
		# @throttleTimeout = ID					# The timeout ID of the delay timer to update a throttled binding
		# @lastUpdate = epoch timestamp			# Last time the deps have been updated; used for throttle functions
		# @hasTransforms = Boolean				# Indicates whether or not at least 1 dependent has a transform (to refrain from executing useless code)
		# @hasConditions = Boolean				# Indicates whether or not at least 1 dependent has a condition (to refrain from executing useless code)
		# @isAsync = Boolean					# Indicates if this is an async binding (currently only used for Event bindings)
		### ========================================================================== ###

		if @type is 'Event' or @type is 'Func'
			@options.updateOnBind = false
			@options.updateEvenIfSame = true


		import [browserOnly] parts/Binding-DOMChoice_group.coffee
		
		unless @type is 'Event' or isProxiedFunc
			@value = @valueOriginal = subjectValue = @fetchDirectValue()
			
			if @type is 'ObjectProp' and not checkIf.isDefined(subjectValue)
				@object[@property] = subjectValue # Define the prop on the object if it non-existent

			
			if @placeholder and not @pholderValues
				@scanForPholders()

	
			@makePropertyLive()
		@attachEvents()


		# IF this is a binding to a specific index of an array then we must make this binding update the array on change if the array binding is tracking its children
		if @object instanceof Array and @type isnt 'Array'
			@arrayBinding = arrayBinding = cache.get(@object, true)
			
			if arrayBinding and arrayBinding.options.trackArrayChildren and not arrayIncludes(arrayBinding.trackedChildren, @property)
				arrayBinding.trackedChildren.push(@property)
				SimplyBind(@property).of(@object).to arrayBinding.updateSelf


		return boundInstances[@ID] = @
















	###*
	 * Stage definitions:
	 * 
	 * 0: Selection:			Got selector, awaiting object.
	 * 1: Indication:			Got object, awaiting proxied property / function / Binding-object.
	 * 2: Binding Selection:	Got proxied selector, awaiting proxied object.
	 * 3: Binding Complete:		Complete, awaiting additional (optional) bindings/mutations.
	###
	BindingInterface = (binding, stage, inheritedState, subject, isProxiedFunc, options)->
		extendState(@, inheritedState) if inheritedState
		@stage = stage or 0
		@proxies ?= []
		@state ?= {}
		# @state.hasTransform
		# @state.hasMultiTransforms
		# @state.initialBinding
		# @state.hasEventName
		
		switch @stage
			when 0
				@optionsPassed = options ||= {}
				@options = {}
				for key of globalOptions
					@options[key] = if options[key]? then options[key] else globalOptions[key]
				


				if checkIf.isFunction(subject)
					@stage = 1
					binding = @createBinding(subject, 'Func', true, isProxiedFunc)
					
				else if subject instanceof Array
					@stage = 1
					binding = @createBinding(subject, 'Array', true)


				else
					subject = subject.toString() if checkIf.isNumber(subject)

					@selector = @property = subject
					unless @options.simpleSelector
						if arrayIncludes(@selector, ':')
							split = @property.split(':')
							@descriptor = split[0] # An addl. string in the selector name defining the scope of the selection (i.e. SimplyBind('attr:class'))
							@property = split[1]
						
						
						if arrayIncludes(@selector, '.') # Placeholder extraction
							split = @property.split('.')
							@property = split[0]				
							@placeholder = split.slice(1).join('.')

						@selector = @property
		



		
			when 1
				unless binding # is defined
					import [browserOnly] parts/BindingInterface-parseDOMObject.coffee

					newObjectType = switch
						when @state.hasEventName then 'Event'
						import [browserOnly] parts/BindingInterface-setType.DOM.coffee
						else 'ObjectProp'

					if @descriptor is 'multi'
						binding = new BindingMulti(subject, newObjectType, @)
					else
						binding = @createBinding(subject, newObjectType)
		


		@defineMainProps(binding)





	# ==== Binding Prototype =================================================================================
	import parts/Binding-prototype.coffee


	# ==== BindingInterface Prototype =================================================================================
	import parts/BindingInterface-private_prototype.coffee
	import parts/BindingInterface-prototype.coffee
	

	# ==== BindingInterface Multi Proxy =================================================================================
	import parts/BindingMulti.coffee


	# ==== SimplyBind props/methods =================================================================================
	import parts/SimplyBind-methods.coffee


	# ==== Exports =================================================================================
	import [browserOnly] parts/SimplyBind-export.window.coffee
	import [nodeOnly] parts/SimplyBind-export.module.coffee
