-# fieldmapper.coffee
+# We define a module to provide services to resolve 
+# inconsistencies between the server and client
+# in how they represent values of a gvein data type
+# and how they structure certain data objects.
 #
-# Field mapping functions:
+# For example, the non-Javascript server will represent a number as 1 or '1'
+# whereas the Javascript client will represent it as the *Number* object.
 #
-# These turn the raw data returned by the server into objects which are easily
-# manipulated, and back again.
+# Moreover, the server will represent a data object
+# as values in an array, with the position of a value in the array corresponding to the field,
+# whereas the client will need to represent the values in an object with the field names.
+# When we convert an array into an object, it is known as 'fieldmapping'
+# and will be accomplished by the *fieldmap* function.
+# The converse operation is accomplished by the *mapfield* function.
 
-# Synchronously load dependents that are not jModules.
+# The module is dependent on Javascript files that are not packaged as modules.
+# Here, we synchronously load them into the module for use.
+# *fmall* will supply a global object *fmclasses* that defines
+# a mapping between position indices and field names.
+# *fm_datatypes* will supply another global object that maps between fields and data types.
 jMod.include 'dojo.fieldmapper.fmall', 'eg.fm_datatypes'
 
 module 'eg.fieldmapper', ->
-	that = @
 
-	identity = (x) -> x
-
-	# Sometimes a value in a fieldmapped object is null. If so, we don't
-	# want to cast it, because we don't want a value of, e.g., "null"
+	# ***
+	# Define a helper function to guard a given function
+	# from being applied against a null argument.
 	guard_null = (fn) -> (x) -> if x? then fn.apply @, arguments else x
 
-	# We don't want to try to cast an error object
-	guard_ret = (fn) ->
-
+	# Define a helper function to guard a given function
+	# from being applied against an argument that is an *ilsevent* object.
+	guard_ilsevent = (fn) ->
 		guard_null (x) ->
 			if typeof x is 'object' and (x.ilsevent isnt undefined or (x[0] and x[0].ilsevent isnt undefined))
 				return x
 			else
 				return fn.apply @, arguments
 
-	@ret_types = {
-		'number': guard_ret Number
-		'string': guard_ret String
-		'search': guard_ret (x) ->
+	# Define a public object to cast data types.
+	@ret_types =
+
+		# We type cast a given input as a number or string
+		# except if it is a null value.
+		'number': guard_ilsevent Number
+		'string': guard_ilsevent String
+
+		# We type cast the number values in a given *search* object
+		# except if it is an *ilsevent* object.
+		'search': guard_ilsevent (x) ->
 			(x[f] = Number x[f]) for f in [
 				'count'
 				'superpage_size'

			if x.__c then _fieldmap(x.__c, x.__p) else {}



	maptype = {
		'':        identity
		'fm':      guard_null (x, cls) => if typeof x is 'object' then @mapfield { cls:x } else x
		'number':  guard_null Number
		'string':  guard_null String
		'date':    identity
		'boolean': (x) -> if x then 't' else 'f'
	}

	# The reverse operation of fieldmap(),
	# Map to an array of values from an object using a map between position indices and field names.
	#	xs                 = [{        mapname:        {'a':1, 'b':2, 'c':3 } }]
	#	fmclasses          =  {        mapname:        ['a',   'b',   'c'   ] }
	#	ys = mapfield(xs)  # [{ '__c': mapname, '__p': [    1,     2,     3 ] }]
	@mapfield = (xs) ->

		a = []
		for m, o of xs
			map = m
			if (ts = fm_datatypes[m])?
				for name, n in fmclasses[m]
					a[n] = if t = ts[name] then maptype[t] o[name], m else o[name]
			else
				for name, n in fmclasses[m]
					a[n] = o[name]

		{ '__c': map, '__p': a }


	# Input fm.flatten_tree( array, [flag] )
	#	fm.flatten_tree( array ) : return the visible children in the tree
	#	fm.flatten_tree( array, flag ): return the complete tree
	@flatten_tree = (o) ->

		_flatten_tree = (os) ->
			a = []
			$.each os, (n, o) ->
				return [] unless o.opac_visible
				a.push o
				(a.push v) for k, v of _flatten_tree o.children if o.children
				delete o.children
			return a

		# We need to deep copy the object
		# otherwise the version in the cache will be modified.
		_flatten_tree $.extend true, {}, [o]

	return @

					'total'
				] when x.superpage_summary[f] isnt undefined
			return x

		# We type cast the number values in a given *prefs* object
		# except if it is an *ilsevent* object.
		'prefs': guard_ilsevent (x) ->
			(x[p] = Number x[p]) for p in [
				'opac.hits_per_page'
				'opac.default_search_location'
				'opac.default_search_depth'
			] when x[p]
			return x

	# ***
	# Define a public function to 'flatten' a given tree of objects.
	# *opac_visible* objects are returned in a list.
	# > FIXME: this could be integrated into *ret_types()* as another type cast operation,
	@flatten_tree = (o) ->

		_flatten_tree = (os) ->
			a = []
			$.each os, (n, o) ->
				return [] unless o.opac_visible
				a.push o
				(a.push v) for k, v of _flatten_tree o.children if o.children
				delete o.children
			return a

		# We need to deep-copy the object,
		# otherwise the version in the cache will be modified.
		_flatten_tree $.extend true, {}, [o]



	# ***
	# We define a helper object for use by the public *fieldmap* function
	# to cast data types for client use.
	# We have to guard against casting only null values.
	typemap =
		'':        (x) -> x
		'fm':      guard_null (x) => if typeof x is 'object' then @fieldmap x else x
		'number':  guard_null Number
		'string':  guard_null String

		# > We will use Date objects to represent dates in the client.
		# For the most part though,
		# dates are displayed to the user as strings,
		# and possibly modified by the user as strings
