module MongoMapper
  module Associations
    class InArrayProxy < Collection
      include ::MongoMapper::Finders
      
      def find(*args)
        options = args.extract_options!
        
        case args.first
          when :first
            first(options)
          when :last
            last(options)
          when :all
            all(options)
          else
            klass.find(*scoped_ids(args) << scoped_options(options))
        end
      end
      
      def find!(*args)
        options = args.extract_options!
        
        case args.first
          when :first
            first(options)
          when :last
            last(options)
          when :all
            all(options)
          else
            klass.find!(*scoped_ids(args) << scoped_options(options))
        end
      end

      def paginate(options)
        klass.paginate(scoped_options(options))
      end

      def all(options={})
        klass.all(scoped_options(options))
      end

      def first(options={})
        klass.first(scoped_options(options))
      end

      def last(options={})
        klass.last(scoped_options(options))
      end
      
      def count(options={})
        options.blank? ? ids.size : klass.count(scoped_options(options))
      end
      
      def destroy_all(options={})
        all(options).each do |doc|
          ids.delete(doc.id)
          doc.destroy
        end
        reset
      end

      def delete_all(options={})
        docs = all(options.merge(:select => ['_id']))
        docs.each { |doc| ids.delete(doc.id) }
        klass.delete(docs.map(&:id))
        reset
      end
      
      def nullify
        replace([])
        reset
      end
      
      def create(attrs={})
        doc = klass.create(attrs)
        unless doc.new?
          ids << doc.id
          owner.save
        end
        doc
      end
      
      def create!(attrs={})
        doc = klass.create!(attrs)
        unless doc.new?
          ids << doc.id
          owner.save
        end
        doc
      end
      
      def <<(*docs)
        flatten_deeper(docs).each do |doc|
          doc.save if doc.new?
          unless ids.include?(doc.id)
            ids << doc.id
          end
        end
        reset
      end
      alias_method :push, :<<
      alias_method :concat, :<<
      
      def replace(docs)
        doc_ids = docs.map do |doc|
          doc.save if doc.new?
          doc.id
        end
        ids.replace(doc_ids.uniq)
        reset
      end
      
      private
        def scoped_conditions
          {:_id => ids}
        end
        
        def scoped_options(options)
          reflection.finder_options.merge(options).merge(scoped_conditions)
        end
        
        def scoped_ids(args)
          args.flatten.reject { |id| !ids.include?(id) }
        end
        
        def find_target
          ids.blank? ? [] : all
        end
        
        def ids
          owner[options[:in]]
        end
    end
  end
end