class URL
  class Service
    class EndpointBuilder
      attr_reader :_endpoint
      extend Forwardable

      # Create a method missing enironment to build out all the methods on the
      # endpoint object
      def initialize base_url, endpoint, params={}, &blk
        @url = base_url.dup
        @url.add_to_path endpoint.to_s
        @url.params.merge!(params)
        @_endpoint = Endpoint.new(@url)
        
        instance_eval &blk if block_given?
      end

      # Allow nesting of endpoints
      def_delegators :@_endpoint, :endpoint

      def returns arg=nil, &blk
        blk = arg unless block_given?
        @_endpoint.inflate_into = blk
      end

      # Build any additional methods sent
      def method_missing *args, &blk
        if m = caller.first.match(/^(#{__FILE__}:\d+):in `method_missing'$/) # protect against a typo within this function creating a stack overflow
          raise "Method missing calling itself with #{args.first} in #{m[1]}"
        end
        is_built_in = false
        # If you define a method named get,post,create,etc don't require the method type
        if Endpoint::BUILT_IN_METHODS.include?(args.first) && !Endpoint::BUILT_IN_METHODS.include?(args[1])
          name = args.shift

          if Endpoint::BUILT_IN_MAP.has_key?(name.to_sym)
            method = name.to_sym
            is_built_in = true
          else
            method = Endpoint::BULT_IN_MAP.find do |meth,aliases|
              aliases.include?(name)
            end

            method = method[0] if method
          end
        else
          name = args.shift
          method = args.shift if args.first.is_a?(Symbol)
        end
        name = name.to_sym

        method ||= :get
        
        options = args.shift||{}
        options[:requires] ||= []
        options[:requires] = [options[:requires]] unless options[:requires].is_a?(Array)
        options[:into] ||= blk if block_given?
        @_endpoint.method_inflate_into[name] = options[:into]

        @_endpoint.class_eval <<-RUBY, __FILE__, __LINE__
          def #{name} force_params={}, args={}
            params = #{(options[:default]||{}).inspect}.merge(force_params)
            #{(options[:requires]||[]).inspect}.each do |req|
              raise RequiredParameter, "#{name} endpoint requires the "<<req<<" paramerter" unless params.include?(req.to_sym) || params.include?(req.to_s)
            end

            if #{is_built_in.inspect}
              super(params,args)
            else
              transform_response(#{method}(params,args),method_inflate_into[#{name.inspect}])
            end
          end
        RUBY
      end

    end
  end
end