module ActsAsApi
  # This module enriches the ActiveRecord::Base module of Rails.
  module Base
    # Indicates if the current model acts as api.
    # False by default.
    def acts_as_api?
      false
    end

    # When invoked, it enriches the current model with the
    # class and instance methods to act as api.
    def acts_as_api

      class_eval do
        include ActsAsApi::Base::InstanceMethods
        extend ActsAsApi::Base::ClassMethods
      end

      if block_given?
        yield ActsAsApi::Config
      end

    end

    module ClassMethods

      def acts_as_api?#:nodoc:
        self.included_modules.include?(InstanceMethods)
      end

      # Determines the attributes, methods of the model that are accessible in the api response.
      # *Note*: There is only whitelisting for api accessible attributes.
      # So once the model acts as api, you have to determine all attributes here that should
      # be contained in the api responses.
      def api_accessible_deprecated(api_templates)
        api_templates.each do |api_template, attributes|
          write_inheritable_attribute("api_accessible_#{api_template}".to_sym, Set.new(attributes) + (api_accessible_attributes(api_template) || []))
        end
      end

      def api_accessible(api_template, options = {}, &block)

        #        return api_accessible_deprecated([api_template]) if api_template.is_a? Hash


        attributes = api_accessible_attributes(api_template) || ApiTemplate.new

        attributes.merge!(api_accessible_attributes(options[:extend])) if options[:extend]

        if block_given?
          yield attributes
        end

        write_inheritable_attribute("api_accessible_#{api_template}".to_sym, attributes)
      end

      # Returns an array of all the attributes that have been made accessible to the api response.
      def api_accessible_attributes(api_template)
        read_inheritable_attribute("api_accessible_#{api_template}".to_sym)
      end

    end


    module InstanceMethods

      # Creates the api response of the model and returns it as a Hash.
      def as_api_response(api_template)
        api_attributes = self.class.api_accessible_attributes(api_template)

        api_output = {}

        return api_output if api_attributes.nil?

        queue = []
        queue << { :parent =>  api_output, :item => api_attributes}

        until queue.empty? do

            leaf = queue.pop

            leaf[:item].each do |k,v|

              case v
              when Symbol

                if self.respond_to?(v)
                  out = send v

                  if out.respond_to?(:as_api_response)
                    out = out.send(:as_api_response, api_template)
                  end

                  leaf[:parent][k] = out

                end

              when Proc
                
                out = v.call(self)
                
                if out.respond_to?(:as_api_response)
                  out = out.send(:as_api_response, api_template)
                end                
                
                leaf[:parent][k] = out

              when String
                # go up the call chain
                out = self
                v.split(".").each do |method|
                  out = out.send(method.to_sym)
                end

                if out.respond_to?(:as_api_response)
                  out = out.send(:as_api_response, api_template)
                end

                leaf[:parent][k] = out

              when Hash
                leaf[:parent][k] ||= {}
                queue << { :parent =>  leaf[:parent][k], :item => v}
              end

            end

          end

          api_output

        end

      end

    end
    
  end