module CounterCache
  class Counter

    attr_reader :owner_class, :counter_name, :foreign_key

    def initialize(name, owner_class, association, options={})
      @counter_name = options[:counter_name] || "#{name.to_s.tableize}_count"
      @foreign_key  = association.foreign_key
      @owner_class  = owner_class
      @backend_name = options[:backend] || :rails
      @backend = CounterCache.initialize_backend(@backend_name, owner_class, counter_name)

    end

    def increment(record_or_id)
      return unless record_or_id
      {:counter => self, :kind => :increment, :id => association_id_for(record_or_id)}
    end

    def decrement(record_or_id)
      return unless record_or_id

      {:counter => self, :kind => :decrement, :id => association_id_for(record_or_id)}
    end

    def reset(record_or_id, count)

      return unless record_or_id
      @backend.reset(owner_class, counter_name, association_id_for(record_or_id), count)
    end

    def change(object)
      if object.send("#{@foreign_key}_changed?")
        old_id = object.changed_attributes[@foreign_key]
        new_id = association_id_for(object)
        if old_id != new_id
          return [increment(new_id), decrement(old_id)]
        end
      end
    end

    def execute(kind, id)
      case kind
      when :increment
        @backend.increment(owner_class, counter_name, id)
      when :decrement
        @backend.decrement(owner_class, counter_name, id)
      else
        raise "Invalid counter change type: #{kind}"
      end
    end

    private

    def association_id_for(record_or_id)
      return record_or_id if record_or_id.is_a?(Integer)

      record_or_id.send(@foreign_key)
    end
  end

end
