'Internal findAndModify helper.'

def __find_and_modify(self, filter, projection, sort, upsert=None, return_document=ReturnDocument.BEFORE, **kwargs):

    common.validate_is_mapping('filter', filter)
    if (not isinstance(return_document, bool)):
        raise ValueError('return_document    must    be    ReturnDocument.BEFORE    or    ReturnDocument.AFTER')
    collation = validate_collation_or_none(kwargs.pop('collation', None))
    cmd = SON([('findAndModify', self.__name), ('query', filter), ('new', return_document)])
    cmd.update(kwargs)
    if (projection is not None):
        cmd['fields'] = helpers._fields_list_to_dict(projection, 'projection')
    if (sort is not None):
        cmd['sort'] = helpers._index_document(sort)
    if (upsert is not None):
        common.validate_boolean('upsert', upsert)
        cmd['upsert'] = upsert
    with self._socket_for_writes() as sock_info:
        if ((sock_info.max_wire_version >= 4) and ('writeConcern' not in cmd)):
            wc_doc = self.write_concern.document
            if wc_doc:
                cmd['writeConcern'] = wc_doc
        out = self._command(sock_info, cmd, read_preference=ReadPreference.PRIMARY, allowable_errors=[_NO_OBJ_ERROR], collation=collation)
        _check_write_command_response([(0, out)])
    return out.get('value')
