module OData
	# Internally used helper class for building a dynamic class.  This class shouldn't be called directly.
	class ClassBuilder
		# Creates a new instance of the ClassBuilder class
		#
		# ==== Required Attributes
		# - klass_name: 	The name/type of the class to create
		# - methods:			The accessor methods to add to the class
		# - nav_props:		The accessor methods to add for navigation properties
		def initialize(klass_name, methods, nav_props)
			@klass_name = klass_name
			@methods = methods
			@nav_props = nav_props
		end
		
		# Returns a dynamically generated class definition based on the constructor parameters
		def build
			# return if already built
		  return @klass unless @klass.nil?
		
		  # need the class name to build class
		  return nil    if @klass_name.nil?
		      
			# return if we can find constant corresponding to class name
			if Object.constants.include? @klass_name
				@klass = @klass_name.constantize
				return @klass
			end
		      
			Object.const_set(@klass_name, Class.new.extend(ActiveSupport::JSON))
		  @klass = @klass_name.constantize
 
			add_methods(@klass)
			add_nav_props(@klass)
			
		  return @klass
		end
		
		private
		def add_methods(klass)
			# Add metadata methods
			klass.send :define_method, :__metadata do
				instance_variable_get("@__metadata")
			end
			klass.send :define_method, :__metadata= do |value|
					instance_variable_set("@__metadata", value)
			end
		  klass.send :define_method, :as_json do |options|
				meta = '__metadata'
				vars = self.instance_values
				
				if !options.nil? && !options[:seen].nil? && vars.has_key?(meta)
					vars.delete_if { |k,v| k != meta}
				else
					vars.delete(meta)
				end

				vars
			end
			
			
			# Add the methods that were passed in
			@methods.each do |method_name|
				klass.send :define_method, method_name do
					instance_variable_get("@#{method_name}")
				end
				klass.send :define_method, "#{method_name}=" do |value|
					instance_variable_set("@#{method_name}", value)
				end
			end
		end
		
		def add_nav_props(klass)
			@nav_props.each do |method_name|
				klass.send :define_method, method_name do
					instance_variable_get("@#{method_name}")
				end
				klass.send :define_method, "#{method_name}=" do |value|
					instance_variable_set("@#{method_name}", value)
				end				
			end
		end
	end
end # module OData