module REXML
	# If you add a method, keep in mind two things:
	# (1) the first argument will always be a list of nodes from which to
	# filter.  In the case of context methods (such as position), the function
	# should return an array with a value for each child in the array.
	# (2) all method calls from XML will have "-" replaced with "_".
	# Therefore, in XML, "local-name()" is identical (and actually becomes)
	# "local_name()"
	module Functions
		@@node = nil
		@@pair = []
		@@variable = {}
		@@namespace_context = {}

		def Functions::node=(value); @@node = value; end
		def Functions::pair=(value); @@pair = value; end
		def Functions::variable=(value); @@variable = value; end
		def Functions::namespace_context=(value)
			@@namespace_context = value
		end
		def Functions::node; @@node; end
		def Functions::pair; @@pair; end
		def Functions::variable; @@variable; end
		def Functions::namespace_context; @@namespace_context; end

		def Functions::text( )
			return true if @@node.kind_of? Text
		end

		def Functions::last( )
			@@pair[1]
		end

		def Functions::position( )
			@@pair[0]
		end

		def Functions::count( node_set )
			node_set.size
		end

		# Since REXML is non-validating, this method is not implemented as it
		# requires a DTD
		def Functions::id( object )
		end

		# NOT TESTED
		def Functions::local_name( node_set=nil )
			get_namespace( node_set ){|node|node.local_name}
		end

		# NOT TESTED
		def Functions::namespace_uri( node_set=nil )
			get_namespace( node_set ) {|node| node.namespace}
		end

		def Functions::name( node_set=nil )
			get_namespace( node_set ) { |node| node.name }
		end

		# Helper method.
		def Functions::get_namespace( node_set = nil )
			if node_set == nil
				yield @@node if @@node.kind_of? Namespace
			else	
				return "" unless node_set.kind_of? Enumerable
				node = node_set.find{|node| node.kind_of? Namespace}
				return "" unless node
				yield node
			end
		end

		# UNTESTED
		def Functions::string( object=nil )
			object = context unless object
			if object.kind_of? Array
				string( object[0] )
			elsif object.kind_of? Text
				object.to_s
			elsif object.kind_of? Element
				object.name
			else
				object.to_s
			end
		end

		# UNTESTED
		def Functions::concat( *objects )
			objects.join
		end

		# UNTESTED
		def Functions::starts_with( string, test )
			string.index(test) == 0
		end

		# UNTESTED
		def Functions::contains( string, test )
			string.include? test
		end

		# UNTESTED
		def Functions::substring_before( string, test )
			string[ 0...string.index(test) ]
		end

		# UNTESTED
		def Functions::substring_after( string, test )
			string[ string.index(test)+1..-1 ]
		end

		# UNTESTED
		def Functions::substring( string, start, length=0 )
			length = string.size if length==0
			string[start-1,length]
		end

		# UNTESTED
		def Functions::string_length( string )
			string.size
		end

		# UNTESTED
		def Functions::normalize_space( string=nil )
			string = string(@@node) if string.nil?
			string.strip.gsub(/\s+/um, ' ')
		end

		# UNTESTED
		def Functions::translate( string, tr1, tr2 )
			string.tr tr1,tr2
		end

		# UNTESTED
		def Functions::boolean( object=nil )
			if object.kind_of? String
				if object =~ /\d+/u
					return object.to_f != 0
				else
					return object.size > 0
				end
			elsif object.kind_of? Array or object.kind_of? Parent
				object.size > 0
			end
		end

		# UNTESTED
		def Functions::not( object )
			return object == "true" if object.is_a? String
			not object
		end

		# UNTESTED
		def Functions::true( )
			true
		end

		# UNTESTED
		def Functions::false(  )
			false
		end

		# UNTESTED
		def Functions::lang( language )
			@@node.collect do |node|
				if node.kind_of? Element
					lang = false
					until node.nil? or lang
						lang = compare_language node.attributes["xml:lang"], language
						node = node.parent
					end
				end
			end
		end

		def Functions::compare_language lang1, lang2
			lang2.downcase.index(lang1.downcase) == 0
		end

		def Functions::number( nodes, object=nil )
		end

		def Functions::sum( nodes )
		end
		
		def Functions::floor( nodes, number )
			number = number.to_f if number.kind_of? String
			number.floor
		end

		def Functions::ceiling( nodes, number )
			number = number.to_f if number.kind_of? String
			number.ceil
		end

		def Functions::round( nodes, number )
			number = number.to_f if number.kind_of? String
			number.round
		end

		def Functions::method_missing( id )
			XPath.match( @@node, id.id2name )
		end
	end
end
