module AbAdmin
  module Controllers
    module Callbacks
      extend ActiveSupport::Concern

      #included do
      #  define_admin_callbacks :build, :create, :update, :save, :destroy
      #end

      protected

      #def build_resource
      #  object = super
      #  run_build_callbacks object
      #  object
      #end

      def create_resource(object)
        run_create_callbacks object do
          save_resource(object)
        end
      end

      def save_resource(object)
        run_save_callbacks object do
          object.save
        end
      end

      def update_resource(object, attributes)
        object.assign_attributes(*attributes)

        #run_update_callbacks object do
          save_resource(object)
        #end
      end

      #def destroy_resource(object)
      #  run_destroy_callbacks object do
      #    object.destroy
      #  end
      #end

      # Simple callback system. Implements before and after callbacks for
      # use within the controllers.
      #
      # We didn't use the ActiveSupport callbacks because they do not support
      # passing in any arbitrary object into the callback method (which we
      # need to do)

      def call_callback_with(method, *args)
        case method
          when Symbol
            send(method, *args)
          when Proc
            instance_exec(*args, &method)
          else
            raise 'Please register with callbacks using a symbol or a block/proc.'
        end
      end

      module ClassMethods

        # Define a new callback.
        #
        # Example:
        #
        #   class MyClassWithCallbacks
        #     include ActiveAdmin::Callbacks
        #
        #     define_admin_callbacks :save
        #
        #     before_save do |arg1, arg2|
        #       # runs before save
        #     end
        #
        #     after_save :call_after_save
        #
        #     def save
        #       # Will run before, yield, then after
        #       run_save_callbacks :arg1, :arg2 do
        #         save!
        #       end
        #     end
        #
        #     protected
        #
        #     def call_after_save(arg1, arg2)
        #       # runs after save
        #     end
        #   end
        #     
        def define_admin_callbacks(*names)
          names.each do |name|
            [:before, :after].each do |type|

              # Define a method to set the callback
              class_eval(<<-EOS, __FILE__, __LINE__ + 1)
              # def self.before_create_callbacks
              def self.#{type}_#{name}_callbacks
                @#{type}_#{name}_callbacks ||= []
              end

              # def self.before_create
              def self.#{type}_#{name}(method = nil, &block)
                #{type}_#{name}_callbacks << (method || block)
              end
              EOS
            end

            # Define a method to run the callbacks
            class_eval(<<-EOS, __FILE__, __LINE__ + 1)
            def run_#{name}_callbacks(*args)
              self.class.before_#{name}_callbacks.each{|callback| call_callback_with(callback, *args)}
              value = yield if block_given?
              self.class.after_#{name}_callbacks.each{|callback| call_callback_with(callback, *args)}
              return value
            end
            EOS
          end
        end
      end
    end
  end
end