'Rename items from a many-one or many-many field such as tags or series.
:param change_index: When renaming in a series-like field also change the series_index values.
:param restrict_to_book_ids: An optional set of book ids for which the rename is to be performed, defaults to all books.'

@write_api
def rename_items(self, field, item_id_to_new_name_map, change_index=True, restrict_to_book_ids=None):

    f = self.fields[field]
    affected_books = set()
    try:
        sv = f.metadata[u'is_multiple'][u'ui_to_list']
    except (TypeError, KeyError, AttributeError):
        sv = None
    if (restrict_to_book_ids is not None):
        if (not isinstance(restrict_to_book_ids, (Set, MutableSet))):
            restrict_to_book_ids = frozenset(restrict_to_book_ids)
        id_map = {}
        default_process_map = {}
        for (old_id, new_name) in item_id_to_new_name_map.iteritems():
            new_names = (tuple((x.strip() for x in new_name.split(sv))) if sv else (new_name,))
            books_with_id = f.books_for(old_id)
            books_to_process = (books_with_id & restrict_to_book_ids)
            if (len(books_with_id) == len(books_to_process)):
                default_process_map[old_id] = new_name
            elif books_to_process:
                affected_books.update(books_to_process)
                newvals = {}
                for book_id in books_to_process:
                    vals = self._field_for(field, book_id)
                    if isinstance(vals, tuple):
                        vals = list(vals)
                        try:
                            dex = vals.index(self._get_item_name(field, old_id))
                            vals[dex] = new_names[0]
                            if (len(new_names) > 1):
                                set_vals = {icu_lower(x) for x in vals}
                                for v in new_names[1:]:
                                    lv = icu_lower(v)
                                    if (lv not in set_vals):
                                        vals.append(v)
                                        set_vals.add(lv)
                            newvals[book_id] = vals
                        except Exception:
                            traceback.print_exc()
                    else:
                        newvals[book_id] = new_names[0]
                self._set_field(field, newvals)
                id_map[old_id] = self._get_item_id(field, new_names[0])
        if default_process_map:
            (ab, idm) = self._rename_items(field, default_process_map, change_index=change_index)
            affected_books.update(ab)
            id_map.update(idm)
        return (affected_books, id_map)
    try:
        func = f.table.rename_item
    except AttributeError:
        raise ValueError((u'Cannot    rename    items    for    one-one    fields:    %s' % field))
    moved_books = set()
    id_map = {}
    for (item_id, new_name) in item_id_to_new_name_map.iteritems():
        new_names = (tuple((x.strip() for x in new_name.split(sv))) if sv else (new_name,))
        (books, new_id) = func(item_id, new_names[0], self.backend)
        affected_books.update(books)
        id_map[item_id] = new_id
        if (new_id != item_id):
            moved_books.update(books)
        if (len(new_names) > 1):
            extra = new_names[1:]
            self._set_field(field, {book_id: (self._fast_field_for(f, book_id) + extra) for book_id in books})
    if affected_books:
        if (field == u'authors'):
            self._set_field(u'author_sort', {k: u'    &    '.join(v) for (k, v) in self._author_sort_strings_for_books(affected_books).iteritems()})
            self._update_path(affected_books, mark_as_dirtied=False)
        elif (change_index and hasattr(f, u'index_field') and (tweaks[u'series_index_auto_increment'] != u'no_change')):
            for book_id in moved_books:
                self._set_field(f.index_field.name, {book_id: self._get_next_series_num_for(self._fast_field_for(f, book_id), field=field)})
        self._mark_as_dirty(affected_books)
    return (affected_books, id_map)
