module MongoMapperExt
  module Storage
    def self.included(model)
      model.class_eval do
        extend ClassMethods
        after_create :_sync_pending_files
      end
    end

    # FIXME: enable metadata. re http://jira.mongodb.org/browse/SERVER-377
    def put_file(filename, io, metadata = {})
      if !new?
        # :metadata => metadata.deep_merge({:_id => self.id})
        GridFS::GridStore.open(self.class.database, filename, "w",
                               :root => self.collection.name,
                               :metadata => {:_id => self.id}) do |f|
          while data = io.read(256)
            f.write(data)
          end
          io.close
        end
      else
        (@_pending_files ||= {})[filename] = io
      end
    end

    def fetch_file(filename)
      if !new?
        MongoMapperExt::File.fetch(self, filename)
      end
    end

    def files
      finder = nil
      if defined?(MongoMapper::FinderOptions)
        finder = MongoMapper::FinderOptions
      else
        finder = MongoMapper::Query
      end

      criteria, options = finder.new(self.class, :metadata => {:_id => self.id}).to_a
      coll = self.class.database.collection("#{self.collection.name}.files")
      @files = coll.find(criteria, options).map do |a|
        MongoMapperExt::File.new(self, a)
      end
    end

    protected
    def _sync_pending_files
      if @_pending_files
        @_pending_files.each do |filename, data|
          put_file(filename, data)
        end
        @_pending_files = nil
      end
    end

    module ClassMethods
      def file_key(name)
        key "_#{name}", String
        define_method("#{name}=") do |file|
          file_id = UUIDTools::UUID.random_create.hexdigest
          filename = name

          if file.respond_to?(:original_filename)
            filename = file.original_filename
          elsif file.respond_to?(:path)
            filename = file.path
          end

          put_file(file_id, file, :original_filename => filename)
          self["_#{name}"] = file_id
        end

        define_method(name) do
          fetch_file(self["_#{name}"]) if self.class.keys.has_key?("_#{name}")
        end

        define_method("has_#{name}?") do
          !self["_#{name}"].blank?
        end
      end
    end
  end
end