/*****************************************************************************
 * VLCLibraryAudioViewController.m: MacOS X interface module
 *****************************************************************************
 * Copyright (C) 2022 VLC authors and VideoLAN
 *
 * Authors: Claudio Cambra <claudio.cambra@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#import "VLCLibraryAudioViewController.h"

#import "extensions/NSString+Helpers.h"
#import "extensions/NSWindow+VLCAdditions.h"

#import "library/VLCLibraryCollectionView.h"
#import "library/VLCLibraryCollectionViewDelegate.h"
#import "library/VLCLibraryCollectionViewFlowLayout.h"
#import "library/VLCLibraryController.h"
#import "library/VLCLibraryDataTypes.h"
#import "library/VLCLibraryModel.h"
#import "library/VLCLibrarySegment.h"
#import "library/VLCLibraryWindowNavigationSidebarViewController.h"
#import "library/VLCLibraryWindowSplitViewController.h"
#import "library/VLCLibraryTwoPaneSplitViewDelegate.h"
#import "library/VLCLibraryWindow.h"
#import "library/VLCLibraryWindowPersistentPreferences.h"

#import "library/audio-library/VLCLibraryAlbumTableCellView.h"
#import "library/audio-library/VLCLibraryAudioDataSource.h"
#import "library/audio-library/VLCLibraryAudioGroupDataSource.h"
#import "library/VLCLibraryHeaderView.h"
#import "library/audio-library/VLCLibraryAudioGroupTableViewDelegate.h"
#import "library/VLCLibraryHeaderCell.h"
#import "library/audio-library/VLCLibraryAudioTableViewDelegate.h"

#import "library/playlist-library/VLCLibraryPlaylistViewController.h"

#import "library/video-library/VLCLibraryVideoViewController.h"

#import "main/VLCMain.h"

#import "windows/video/VLCVoutView.h"
#import "windows/video/VLCMainVideoViewController.h"

NSString *VLCLibraryPlaceholderAudioViewIdentifier = @"VLCLibraryPlaceholderAudioViewIdentifier";

@interface VLCLibraryAudioViewController()
{
    id<VLCMediaLibraryItemProtocol> _awaitingPresentingLibraryItem;

    NSArray<NSLayoutConstraint *> *_internalPlaceholderImageViewSizeConstraints;
    NSArray<NSString *> *_placeholderImageNames;
    NSArray<NSString *> *_placeholderLabelStrings;

    VLCLibraryCollectionViewDelegate *_audioLibraryCollectionViewDelegate;
    VLCLibraryAudioTableViewDelegate *_audioLibraryTableViewDelegate;
    VLCLibraryAudioGroupTableViewDelegate *_audioGroupLibraryTableViewDelegate;
    VLCLibraryTwoPaneSplitViewDelegate *_splitViewDelegate;
}
@end

@implementation VLCLibraryAudioViewController

#pragma mark - Set up the view controller

- (instancetype)initWithLibraryWindow:(VLCLibraryWindow *)libraryWindow
{
    self = [super initWithLibraryWindow:libraryWindow];

    if (self) {
        [self setupPropertiesFromLibraryWindow:libraryWindow];
        [self setupAudioDataSource];

        _audioLibraryCollectionViewDelegate = [[VLCLibraryCollectionViewDelegate alloc] init];
        _audioLibraryTableViewDelegate = [[VLCLibraryAudioTableViewDelegate alloc] init];
        _audioGroupLibraryTableViewDelegate = [[VLCLibraryAudioGroupTableViewDelegate alloc] init];
        _splitViewDelegate = [[VLCLibraryTwoPaneSplitViewDelegate alloc] init];

        [self setupAudioPlaceholderView];
        [self setupAudioCollectionView];
        [self setupGridModeSplitView];
        [self setupAudioTableViews];
        [self setupAudioLibraryViews];

        NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
        [notificationCenter addObserver:self
                               selector:@selector(libraryModelUpdated:)
                                   name:VLCLibraryModelAudioMediaListReset
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(libraryModelUpdated:)
                                   name:VLCLibraryModelAudioMediaItemDeleted
                                 object:nil];

        NSString * const audioMediaResetLongLoadStartNotification = [VLCLibraryModelAudioMediaListReset stringByAppendingString:VLCLongNotificationNameStartSuffix];
        NSString * const audioMediaResetLongLoadFinishNotification = [VLCLibraryModelAudioMediaListReset stringByAppendingString:VLCLongNotificationNameFinishSuffix];
        NSString * const audioMediaDeletedLongLoadStartNotification = [VLCLibraryModelAudioMediaItemDeleted stringByAppendingString:VLCLongNotificationNameStartSuffix];
        NSString * const audioMediaDeletedLongLoadFinishNotification = [VLCLibraryModelAudioMediaItemDeleted stringByAppendingString:VLCLongNotificationNameFinishSuffix];

        [notificationCenter addObserver:self
                               selector:@selector(libraryModelLongLoadStarted:)
                                   name:audioMediaResetLongLoadStartNotification
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(libraryModelLongLoadFinished:)
                                   name:audioMediaResetLongLoadFinishNotification
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(libraryModelLongLoadStarted:)
                                   name:audioMediaDeletedLongLoadStartNotification
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(libraryModelLongLoadFinished:)
                                   name:audioMediaDeletedLongLoadFinishNotification
                                 object:nil];
    }

    return self;
}

- (void)setupPropertiesFromLibraryWindow:(VLCLibraryWindow*)libraryWindow
{
    NSParameterAssert(libraryWindow);

    _audioLibraryView = libraryWindow.audioLibraryView;
    _audioLibrarySplitView = libraryWindow.audioLibrarySplitView;
    _audioCollectionSelectionTableViewScrollView = libraryWindow.audioCollectionSelectionTableViewScrollView;
    _audioCollectionSelectionTableView = libraryWindow.audioCollectionSelectionTableView;
    _audioGroupSelectionTableViewScrollView = libraryWindow.audioGroupSelectionTableViewScrollView;
    _audioGroupSelectionTableView = libraryWindow.audioGroupSelectionTableView;
    _audioSongTableViewScrollView = libraryWindow.audioLibrarySongsTableViewScrollView;
    _audioSongTableView = libraryWindow.audioLibrarySongsTableView;
    _audioCollectionViewScrollView = libraryWindow.audioCollectionViewScrollView;
    _audioLibraryCollectionView = libraryWindow.audioLibraryCollectionView;
    _audioLibraryGridModeSplitView = libraryWindow.audioLibraryGridModeSplitView;
    _audioLibraryGridModeSplitViewListTableViewScrollView = libraryWindow.audioLibraryGridModeSplitViewListTableViewScrollView;
    _audioLibraryGridModeSplitViewListTableView = libraryWindow.audioLibraryGridModeSplitViewListTableView;
    _audioLibraryGridModeSplitViewListSelectionCollectionViewScrollView = libraryWindow.audioLibraryGridModeSplitViewListSelectionCollectionViewScrollView;
    _audioLibraryGridModeSplitViewListSelectionCollectionView = libraryWindow.audioLibraryGridModeSplitViewListSelectionCollectionView;
}

- (void)setupAudioDataSource
{
    _audioDataSource = [[VLCLibraryAudioDataSource alloc] init];
    _audioDataSource.libraryModel = VLCMain.sharedInstance.libraryController.libraryModel;
    _audioDataSource.headerDelegate = self;
    [_audioDataSource setup];

    _audioGroupDataSource = [[VLCLibraryAudioGroupDataSource alloc] init];
    _audioDataSource.audioGroupDataSource = _audioGroupDataSource;
}

- (void)setupAudioCollectionView
{
    _audioDataSource.collectionView = _audioLibraryCollectionView;
    _audioLibraryCollectionView.dataSource = _audioDataSource;
    _audioLibraryCollectionView.delegate = _audioLibraryCollectionViewDelegate;

    _audioLibraryCollectionView.selectable = YES;
    _audioLibraryCollectionView.allowsMultipleSelection = YES;
    _audioLibraryCollectionView.allowsEmptySelection = YES;
    _audioLibraryCollectionView.collectionViewLayout = VLCLibraryCollectionViewFlowLayout.standardLayout;
}

- (void)setupAudioTableViews
{
    _audioLibrarySplitView.delegate = _splitViewDelegate;

    _audioDataSource.collectionSelectionTableView = _audioCollectionSelectionTableView;
    _audioCollectionSelectionTableView.dataSource = _audioDataSource;
    _audioCollectionSelectionTableView.delegate = _audioLibraryTableViewDelegate;

    _audioCollectionHeaderView = [VLCLibraryHeaderView paddedHeaderView];
    _audioGroupSelectionTableView.headerView = self.audioCollectionHeaderView;
    _audioGroupSelectionTableView.tableColumns.firstObject.headerCell = [VLCLibraryHeaderCell new];

    _audioGroupSelectionTableView.dataSource = _audioGroupDataSource;
    _audioGroupDataSource.tableViews = @[_audioGroupSelectionTableView];
    _audioGroupSelectionTableView.delegate = _audioGroupLibraryTableViewDelegate;

    if(@available(macOS 11.0, *)) {
        _audioGroupSelectionTableView.style = NSTableViewStyleFullWidth;
    }

    _audioDataSource.songsTableView = _audioSongTableView;
    _audioSongTableView.dataSource = _audioDataSource;

    _audioDataSource.gridModeListTableView = _audioLibraryGridModeSplitViewListTableView;
    _audioLibraryGridModeSplitViewListTableView.dataSource = _audioDataSource;

    _audioLibraryGridModeSplitViewListSelectionCollectionView.dataSource = _audioGroupDataSource;
    _audioGroupDataSource.collectionView = _audioLibraryGridModeSplitViewListSelectionCollectionView;

    _audioSongTableView.delegate = _audioLibraryTableViewDelegate;

    [_audioDataSource applySelectionForTableView:_audioCollectionSelectionTableView];
}

- (void)setupGridModeSplitView
{
    _audioLibraryGridModeSplitView.delegate = _splitViewDelegate;

    _audioLibraryGridModeSplitViewListTableView.dataSource = _audioDataSource;
    _audioLibraryGridModeSplitViewListTableView.delegate = _audioLibraryTableViewDelegate;

    _audioLibraryGridModeSplitViewListSelectionCollectionView.dataSource = _audioGroupDataSource;
    _audioLibraryGridModeSplitViewListSelectionCollectionView.delegate = _audioLibraryCollectionViewDelegate;

    _audioLibraryGridModeSplitViewListSelectionCollectionView.selectable = YES;
    _audioLibraryGridModeSplitViewListSelectionCollectionView.allowsMultipleSelection = YES;
    _audioLibraryGridModeSplitViewListSelectionCollectionView.allowsEmptySelection = YES;

    VLCLibraryCollectionViewFlowLayout * const audioLibraryGridModeListSelectionCollectionViewLayout =
        VLCLibraryCollectionViewFlowLayout.standardLayout;
    _audioLibraryGridModeSplitViewListSelectionCollectionView.collectionViewLayout = audioLibraryGridModeListSelectionCollectionViewLayout;
    audioLibraryGridModeListSelectionCollectionViewLayout.headerReferenceSize =
        NSMakeSize(self.audioCollectionViewScrollView.documentView.frame.size.width, VLCLibraryHeaderViewHeight);

    if (@available(macOS 10.12, *)) {
        audioLibraryGridModeListSelectionCollectionViewLayout.sectionHeadersPinToVisibleBounds = YES;
    }

    [VLCLibraryAudioDataSource setupCollectionView:_audioLibraryGridModeSplitViewListSelectionCollectionView];
    [VLCLibraryAudioGroupDataSource setupCollectionView:_audioLibraryGridModeSplitViewListSelectionCollectionView];
}

- (void)setupAudioPlaceholderView
{
    _internalPlaceholderImageViewSizeConstraints = @[
        [NSLayoutConstraint constraintWithItem:self.placeholderImageView
                                     attribute:NSLayoutAttributeWidth
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:0.f
                                      constant:149.f],
        [NSLayoutConstraint constraintWithItem:self.placeholderImageView
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:0.f
                                      constant:149.f],
    ];

    _placeholderImageNames = @[@"placeholder-group2", @"placeholder-music", @"placeholder-music", @"placeholder-music"];
    _placeholderLabelStrings = @[
        _NS("Your music artists will appear here.\nGo to the Browse section to add artists you love."),
        _NS("Your music albums will appear here.\nGo to the Browse section to add albums you love."),
        _NS("Your music tracks will appear here.\nGo to the Browse section to add tracks you love."),
        _NS("Your music genres will appear here.\nGo to the Browse section to add genres you love."),
    ];
}

- (void)setupAudioLibraryViews
{
    _audioCollectionSelectionTableView.rowHeight = VLCLibraryUIUnits.mediumTableViewRowHeight;
    _audioLibraryGridModeSplitViewListTableView.rowHeight = VLCLibraryUIUnits.mediumTableViewRowHeight;
    _audioGroupSelectionTableView.rowHeight = VLCLibraryAlbumTableCellView.defaultHeight;

    const NSEdgeInsets audioScrollViewContentInsets = VLCLibraryUIUnits.libraryViewScrollViewContentInsets;
    const NSEdgeInsets audioScrollViewScrollerInsets = VLCLibraryUIUnits.libraryViewScrollViewScrollerInsets;

    _audioCollectionViewScrollView.automaticallyAdjustsContentInsets = NO;
    _audioCollectionViewScrollView.contentInsets = audioScrollViewContentInsets;
    _audioCollectionViewScrollView.scrollerInsets = audioScrollViewScrollerInsets;

    _audioCollectionSelectionTableViewScrollView.automaticallyAdjustsContentInsets = NO;
    _audioCollectionSelectionTableViewScrollView.contentInsets = audioScrollViewContentInsets;
    _audioCollectionSelectionTableViewScrollView.scrollerInsets = audioScrollViewScrollerInsets;
    _audioGroupSelectionTableViewScrollView.automaticallyAdjustsContentInsets = NO;
    _audioGroupSelectionTableViewScrollView.contentInsets = VLCLibraryUIUnits.libraryViewScrollViewDetailListContentInsets;
    _audioGroupSelectionTableViewScrollView.scrollerInsets = audioScrollViewScrollerInsets;

    _audioLibraryGridModeSplitViewListTableViewScrollView.automaticallyAdjustsContentInsets = NO;
    _audioLibraryGridModeSplitViewListTableViewScrollView.contentInsets = audioScrollViewContentInsets;
    _audioLibraryGridModeSplitViewListTableViewScrollView.scrollerInsets = audioScrollViewScrollerInsets;

    _audioLibraryGridModeSplitViewListSelectionCollectionViewScrollView.automaticallyAdjustsContentInsets = NO;
    _audioLibraryGridModeSplitViewListSelectionCollectionViewScrollView.contentInsets = VLCLibraryUIUnits.libraryViewScrollViewDetailGridContentInsets;
    _audioLibraryGridModeSplitViewListSelectionCollectionViewScrollView.scrollerInsets = audioScrollViewScrollerInsets;

    // Songs table view needs bottom padding for controls bar
    const CGFloat controlsBarHeight = VLCLibraryUIUnits.libraryWindowControlsBarHeight;
    const CGFloat controlsBarPadding = VLCLibraryUIUnits.largeSpacing * 2;
    NSClipView * const clipView = _audioSongTableViewScrollView.contentView;
    const CGFloat topInset = VLCLibraryUIUnits.libraryWindowContentSafeTopInset + self.audioSongTableView.headerView.frame.size.height;
    clipView.automaticallyAdjustsContentInsets = NO;
    clipView.contentInsets = NSEdgeInsetsMake(topInset, 0, controlsBarHeight + controlsBarPadding, 0);
}

#pragma mark - Superclass property overrides

- (NSArray<NSLayoutConstraint *> *)placeholderImageViewSizeConstraints
{
    return _internalPlaceholderImageViewSizeConstraints;
}

- (id<VLCLibraryDataSource>)currentDataSource
{
    return self.audioDataSource;
}

#pragma mark - Show the audio view

- (void)presentAudioView
{
    [self updatePresentedView];
}

- (void)presentPlaceholderAudioView
{
    const NSInteger selectedLibrarySegment = self.audioDataSource.audioLibrarySegment;
    NSAssert(selectedLibrarySegment != VLCAudioLibraryRecentsSegment &&
             selectedLibrarySegment != VLCAudioLibraryUnknownSegment,
             @"Received invalid audio library segment from audio data source!");
    [self.libraryWindow displayLibraryPlaceholderViewWithImage:[NSImage imageNamed:_placeholderImageNames[selectedLibrarySegment]]
                                              usingConstraints:self.placeholderImageViewSizeConstraints
                                             displayingMessage:_placeholderLabelStrings[selectedLibrarySegment]];
}

- (void)hideAllViews
{
    _audioLibrarySplitView.hidden = YES;
    _audioLibraryGridModeSplitView.hidden = YES;
    _audioSongTableViewScrollView.hidden = YES;
    _audioCollectionViewScrollView.hidden = YES;
}

- (void)presentAudioGridModeView
{
    const VLCLibrarySegmentType selectedSegment = self.libraryWindow.librarySegmentType;
    if (selectedSegment == VLCLibrarySongsMusicSubSegmentType ||
        selectedSegment == VLCLibraryAlbumsMusicSubSegmentType) {

        [_audioLibraryCollectionView deselectAll:self];
        [(VLCLibraryCollectionViewFlowLayout *)_audioLibraryCollectionView.collectionViewLayout resetLayout];

        _audioCollectionViewScrollView.hidden = NO;
    } else {
        _audioLibraryGridModeSplitView.hidden = NO;
        [_splitViewDelegate resetDefaultSplitForSplitView:self.audioLibraryGridModeSplitView];
    }
}

- (void)presentAudioTableView
{
    if (self.libraryWindow.librarySegmentType == VLCLibrarySongsMusicSubSegmentType) {
        _audioSongTableViewScrollView.hidden = NO;
    } else {
        _audioLibrarySplitView.hidden = NO;
        [_splitViewDelegate resetDefaultSplitForSplitView:self.audioLibrarySplitView];
    }
}

- (VLCLibraryViewModeSegment)viewModeSegmentForCurrentLibrarySegment
{
    VLCLibraryWindowPersistentPreferences * const libraryWindowPrefs = VLCLibraryWindowPersistentPreferences.sharedInstance;

    switch (self.libraryWindow.librarySegmentType) {
        case VLCLibraryArtistsMusicSubSegmentType:
            return libraryWindowPrefs.artistLibraryViewMode;
        case VLCLibraryGenresMusicSubSegmentType:
            return libraryWindowPrefs.genreLibraryViewMode;
        case VLCLibrarySongsMusicSubSegmentType:
            return libraryWindowPrefs.songsLibraryViewMode;
        case VLCLibraryAlbumsMusicSubSegmentType:
            return libraryWindowPrefs.albumLibraryViewMode;
        default:
            return VLCLibraryGridViewModeSegment;
    }
}

- (VLCAudioLibrarySegment)currentLibrarySegmentToAudioLibrarySegment
{
    switch (self.libraryWindow.librarySegmentType) {
        case VLCLibraryMusicSegmentType:
        case VLCLibraryArtistsMusicSubSegmentType:
            return VLCAudioLibraryArtistsSegment;
        case VLCLibraryAlbumsMusicSubSegmentType:
            return VLCAudioLibraryAlbumsSegment;
        case VLCLibrarySongsMusicSubSegmentType:
            return VLCAudioLibrarySongsSegment;
        case VLCLibraryGenresMusicSubSegmentType:
            return VLCAudioLibraryGenresSegment;
        default:
            NSAssert(false, @"Non-audio or unknown library subsegment received");
            return VLCAudioLibraryUnknownSegment;
    }
}

- (void)updatePresentedView
{
    self.audioDataSource.audioLibrarySegment = [self currentLibrarySegmentToAudioLibrarySegment];
    const BOOL anyAudioMedia = self.audioDataSource.libraryModel.numberOfAudioMedia > 0;

    if (anyAudioMedia) {
        [self.libraryWindow displayLibraryView:self.audioLibraryView];
        [self hideAllViews];

        const VLCLibraryViewModeSegment viewModeSegment = [self viewModeSegmentForCurrentLibrarySegment];

        if (viewModeSegment == VLCLibraryListViewModeSegment) {
            [self presentAudioTableView];
        } else if (viewModeSegment == VLCLibraryGridViewModeSegment) {
            [self presentAudioGridModeView];
        } else {
            NSAssert(false, @"View mode must be grid or list mode");
        }

        [VLCMain.sharedInstance.libraryWindow updateGridVsListViewModeSegmentedControl];
    } else if (self.audioDataSource.libraryModel.filterString.length > 0) {
        [self.libraryWindow displayNoResultsMessage];
    } else {
        [self presentPlaceholderAudioView];
    }
}

- (void)reloadData
{
    [_audioDataSource reloadData];
}

- (void)presentLibraryItemInTableView:(id<VLCMediaLibraryItemProtocol>)libraryItem
{
    if (libraryItem == nil) {
        return;
    }

    NSTableView *targetMainTableView;
    if ([libraryItem isKindOfClass:VLCMediaLibraryMediaItem.class]) {
        targetMainTableView = self.audioSongTableView;
    } else if ([self viewModeSegmentForCurrentLibrarySegment] == VLCLibraryGridViewModeSegment) {
        targetMainTableView = self.audioLibraryGridModeSplitViewListTableView;
    } else {
        targetMainTableView = self.audioCollectionSelectionTableView;
    }
    NSAssert(targetMainTableView != nil, @"Target tableview for presenting audio library view is nil");
    NSAssert(targetMainTableView.dataSource == self.audioDataSource, @"Target tableview data source is unexpected");

    const NSInteger rowForLibraryItem = [self.audioDataSource rowForLibraryItem:libraryItem];
    if (rowForLibraryItem != NSNotFound) {
        NSIndexSet * const indexSet = [NSIndexSet indexSetWithIndex:rowForLibraryItem];
        [self.audioDataSource tableView:targetMainTableView selectRowIndices:indexSet];
    }
}

- (void)presentLibraryItemInCollectionView:(id<VLCMediaLibraryItemProtocol>)libraryItem
{
    if (libraryItem == nil) {
        return;
    }

    // If we are handling a library item that is shown in one of the split view modes,
    // with a table view on the left and the collection view on the right, defer back
    // to presentLibraryItemInTabelView
    if ([libraryItem isKindOfClass:VLCMediaLibraryGenre.class] ||
        [libraryItem isKindOfClass:VLCMediaLibraryArtist.class]) {
        [self presentLibraryItemInTableView:libraryItem];
    }

    NSIndexPath * const indexPathForLibraryItem = [self.audioDataSource indexPathForLibraryItem:libraryItem];
    if (indexPathForLibraryItem) {
        NSSet<NSIndexPath *> * const indexPathSet = [NSSet setWithObject:indexPathForLibraryItem];
        NSCollectionView * const collectionView = self.audioLibraryCollectionView;
        VLCLibraryCollectionViewFlowLayout * const expandableFlowLayout = (VLCLibraryCollectionViewFlowLayout *)collectionView.collectionViewLayout;

        [collectionView selectItemsAtIndexPaths:indexPathSet
                                 scrollPosition:NSCollectionViewScrollPositionTop];
        [expandableFlowLayout expandDetailSectionAtIndex:indexPathForLibraryItem];
    }
}

- (void)presentLibraryItemWaitForDataSourceFinished:(nullable NSNotification *)aNotification
{
    if ((NSUInteger)self.audioDataSource.displayedCollectionCount < self.audioDataSource.collectionToDisplayCount) {
        return;
    }
    
    [NSNotificationCenter.defaultCenter removeObserver:self
                                                  name:VLCLibraryAudioDataSourceDisplayedCollectionChangedNotification
                                                object:self.audioDataSource];

    const VLCLibraryViewModeSegment viewModeSegment = [self viewModeSegmentForCurrentLibrarySegment];
    if (viewModeSegment == VLCLibraryListViewModeSegment) {
        [self presentLibraryItemInTableView:_awaitingPresentingLibraryItem];
    } else if (viewModeSegment == VLCLibraryGridViewModeSegment) {
        [self presentLibraryItemInCollectionView:_awaitingPresentingLibraryItem];
    } else {
        NSAssert(NO, @"No valid view mode segment acquired, cannot present item!");
    }

    _awaitingPresentingLibraryItem = nil;
}

- (void)presentLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
{
    if (libraryItem == nil) {
        return;
    }

    _awaitingPresentingLibraryItem = libraryItem;

    // If the library item is a media item, we need to select the corresponding segment
    // in the segmented control. We then need to update the presented view.
    VLCLibrarySegmentType segmentType;
    if ([libraryItem isKindOfClass:VLCMediaLibraryAlbum.class]) {
        segmentType = VLCLibraryAlbumsMusicSubSegmentType;
    } else if ([libraryItem isKindOfClass:VLCMediaLibraryArtist.class]) {
        segmentType = VLCLibraryArtistsMusicSubSegmentType;
    } else if ([libraryItem isKindOfClass:VLCMediaLibraryGenre.class]) {
        segmentType = VLCLibraryGenresMusicSubSegmentType;
    } else {
        segmentType = VLCLibrarySongsMusicSubSegmentType;
    }

    VLCLibraryWindow * const libraryWindow = self.libraryWindow;
    if (segmentType == libraryWindow.librarySegmentType) {
        [self presentLibraryItemWaitForDataSourceFinished:nil];
        return;
    }

    [NSNotificationCenter.defaultCenter addObserver:self
                                           selector:@selector(presentLibraryItemWaitForDataSourceFinished:)
                                               name:VLCLibraryAudioDataSourceDisplayedCollectionChangedNotification
                                             object:self.audioDataSource];

    libraryWindow.librarySegmentType = segmentType;
    [libraryWindow.splitViewController.navSidebarViewController selectSegment:segmentType];
}

- (void)libraryModelUpdated:(NSNotification *)aNotification
{
    VLCLibraryModel * const model = VLCMain.sharedInstance.libraryController.libraryModel;
    const NSUInteger audioCount = model.numberOfAudioMedia;

    if ((self.libraryWindow.librarySegmentType == VLCLibraryMusicSegmentType ||
         self.libraryWindow.librarySegmentType == VLCLibrarySongsMusicSubSegmentType ||
         self.libraryWindow.librarySegmentType == VLCLibraryArtistsMusicSubSegmentType ||
         self.libraryWindow.librarySegmentType == VLCLibraryAlbumsMusicSubSegmentType ||
         self.libraryWindow.librarySegmentType == VLCLibraryGenresMusicSubSegmentType) &&
        ((audioCount == 0 && ![self.libraryTargetView.subviews containsObject:self.emptyLibraryView]) ||
         (audioCount > 0 && ![self.libraryTargetView.subviews containsObject:_audioLibraryView])) &&
        !self.libraryWindow.embeddedVideoPlaybackActive) {

        [self updatePresentedView];
    }
}

- (void)libraryModelLongLoadStarted:(NSNotification *)notification
{
    if (self.connected) {
        [self.audioDataSource disconnect];
        [self.audioGroupDataSource disconnect];
    }

    [self.libraryWindow showLoadingOverlay];
}

- (void)libraryModelLongLoadFinished:(NSNotification *)notification
{
    if (self.connected) {
        [self.audioDataSource connect];
        [self.audioGroupDataSource connect];
    }

    [self.libraryWindow hideLoadingOverlay];
}

- (void)updateHeaderForTableView:(NSTableView *)tableView
             withRepresentedItem:(nullable VLCLibraryRepresentedItem *)representedItem
                   fallbackTitle:(NSString *)fallbackTitle
                  fallbackDetail:(NSString *)fallbackDetail
{
    if (tableView != self.audioCollectionSelectionTableView &&
        tableView != self.audioGroupSelectionTableView &&
        ![self.audioGroupDataSource.tableViews containsObject:tableView])
        return;

    if (representedItem != nil) {
        self.audioCollectionHeaderView.representedItem = representedItem;
    } else {
        [self.audioCollectionHeaderView updateWithRepresentedItem:nil
                                                    fallbackTitle:fallbackTitle
                                                   fallbackDetail:fallbackDetail];
    }
}

@end
