/*
SRRSSManager.m

Author: Makoto Kinoshita

Copyright 2004 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "SRAppDelegate.h"
#import "SRBookmark.h"
#import "SRBookmarkStorage.h"
#import "SRRSSManager.h"
#import "SRDefaultsKey.h"

#import "SRSDSyndication.h"

#import "FoundationEx.h"

// Notifications
NSString*    SRRSSWillStartRefresh = @"SRRSSWillStartRefresh";
NSString*    SRRSSProgressRefresh = @"SRRSSProgressRefresh";
NSString*    SRRSSDidEndRefresh = @"SRRSSDidEndRefresh";
NSString*    SRRSSItemsRemovedAll = @"SRRSSItemsRemovedAll";
NSString*    SRRSSItemsPreviewed = @"SRRSSItemsPreviewed";
NSString*    SRRSSFeedsDidChanged = @"SRRSSFeedsDidChanged";

@implementation SRRSSManager : NSObject

//--------------------------------------------------------------//
#pragma mark -- Initialize --
//--------------------------------------------------------------//

+ (SRRSSManager*)sharedInstance
{
    static SRRSSManager*    _sharedInstance = nil;
    if (!_sharedInstance) {
        _sharedInstance = [[SRRSSManager alloc] init];
    }
    
    return _sharedInstance;
}

- (id)init
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize member variables
    _isSyndicationWorking = NO;
    _newComingArticles = 0;
    _RSSChannels = [[NSMutableArray array] retain];
    _RSSItems = [[NSMutableArray array] retain];
    _notUpdateFeeds = [[NSMutableArray array] retain];
    _cachedChannelDict = [[NSMutableDictionary dictionary] retain];
    
    // Load RSS channels
    [self reloadFeeds];
    
    // Register notifications
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
            selector:@selector(taskDidTerminate:) name:NSTaskDidTerminateNotification object:nil];
    [center addObserver:self 
            selector:@selector(bookmarkAdded:) name:SRBookmarkAddedNotificationName object:nil];
    [center addObserver:self 
            selector:@selector(bookmarkRemoved:) name:SRBookmarkRemovedNotificationName object:nil];
    [center addObserver:self 
            selector:@selector(bookmarkChanged:) name:SRBookmarkChangedNotificationName object:nil];
    [center addObserver:self 
            selector:@selector(bookmarkRSSAutoUpdateChanged:) name:SRBookmarkRSSAutoUpdateChanged object:nil];
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRRSSUpdate options:NSKeyValueObservingOptionNew context:NULL];
    
    // Register distributed notifications
    NSDistributedNotificationCenter*    distributedCenter;
    distributedCenter = [NSDistributedNotificationCenter defaultCenter];
    [distributedCenter addObserver:self 
            selector:@selector(sdFeedWillStartRefresh:) name:SRSDFeedWillStartRefresh object:nil];
    [distributedCenter addObserver:self 
            selector:@selector(sdFeedProgressRefresh:) name:SRSDFeedProgressRefresh object:nil];
    [distributedCenter addObserver:self 
            selector:@selector(sdFeedDidEndRefresh:) name:SRSDFeedDidEndRefresh object:nil];
    
    return self;
}

- (void)dealloc
{
    [self cancelUpdateTimer];
    
    [_RSSChannels release];
    [_RSSItems release];
    [_notUpdateFeeds release];
    [_cachedChannelDict release];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Application support folder --
//--------------------------------------------------------------//

+ (NSString*)feedsFolderPath
{
    return [[[NSApp delegate] libraryFolder] stringByAppendingPathComponent:@"Feeds"];
}

+ (NSString*)feedsFilePath
{
    return [[self feedsFolderPath] stringByAppendingPathComponent:@"Feeds.plist"];
}

+ (NSString*)notUpdateFeedsFilePath
{
    return [[self feedsFolderPath] stringByAppendingPathComponent:@"NotUpdateFeeds.plist"];
}

//--------------------------------------------------------------//
#pragma mark -- RSS syndication --
//--------------------------------------------------------------//

- (BOOL)isRSSSyndicationWorking
{
    return _isSyndicationWorking;
}

- (void)launchRSSSyndicationWithArguments:(NSArray*)arguments
{
    // Check working flag
    if (_isSyndicationWorking) {
        return;
    }
    
    // Get path
    NSString*   syndicationPath;
    syndicationPath = [[NSBundle mainBundle] bundlePath];
    syndicationPath = [syndicationPath stringByAppendingPathComponent:
            @"Contents/Support Applications/RSS Syndication.app/Contents/MacOS/RSS Syndication"];
    
    // Launch RSS syndication
    if (_RSSTask) {
        if ([_RSSTask isRunning]) {
            [_RSSTask terminate];
        }
        [_RSSTask release];
    }
    _RSSTask = [[NSTask launchedTaskWithLaunchPath:syndicationPath arguments:arguments] retain];
    
    // Set priority
    int pid;
    pid = [_RSSTask processIdentifier];
    if (pid > 0) {
        setpriority(PRIO_PROCESS, pid, 10);
    }
}

- (void)refreshFeeds:(NSArray*)feedURLs
{
    // Check working flag
    if (_isSyndicationWorking) {
        return;
    }
    
    _newComingArticles = 0;
    
    // Launch RSS syndication
    NSArray*    arguments;
    arguments = [NSArray arrayWithObjects:
            [feedURLs description], 
            [NSString stringWithFormat:@"%d", [[NSUserDefaults standardUserDefaults] integerForKey:SRRSSRemove]], 
            nil];
    [self launchRSSSyndicationWithArguments:arguments];
}

- (void)refreshAllFeeds
{
    // Check working flag
    if (_isSyndicationWorking) {
        return;
    }
    
    // Get RSS bookmarks
    NSArray*    RSSBookmarks;
    RSSBookmarks = [[SRBookmarkStorage sharedInstance] RSSBookmarks];
    if ([RSSBookmarks count] == 0) {
        return;
    }
    
    // Collect feed URLs
    NSMutableArray* feedURLs;
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    feedURLs = [NSMutableArray array];
    enumerator = [RSSBookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        if (![bookmark isAutoUpdate]) {
            continue;
        }
        
        NSString*   URLString;
        URLString = [bookmark URLString];
        if ([URLString hasPrefix:@"feed://"]) {
            URLString = [NSString stringWithFormat:@"http://%@", [URLString substringFromIndex:7]];
        }
        
        [feedURLs addObject:URLString];
    }
    
    // Refresh feeds
    [self refreshFeeds:feedURLs];
}

- (void)stopRefresh
{
    // Post cancel request
    [[NSDistributedNotificationCenter defaultCenter] 
            postNotificationName:SRSDFeedRequestedCancelRefresh 
            object:nil 
            userInfo:nil]; 
}

- (void)deleteAllItems
{
    // Delete items of channels
    NSEnumerator*           enumerator;
    NSMutableDictionary*    channel;
    enumerator = [_RSSChannels objectEnumerator];
    while (channel = [enumerator nextObject]) {
        [[channel objectForKey:@"items"] removeAllObjects];
    }
    
    // Delete items
    [_RSSItems removeAllObjects];
    
    // Notify it
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRRSSItemsRemovedAll object:self];
}

- (void)terminateRSSSyndication
{
    // Terminate task
    if (_RSSTask && [_RSSTask isRunning]) {
        [_RSSTask terminate];
    }
}

//--------------------------------------------------------------//
#pragma mark -- RSS channels and items --
//--------------------------------------------------------------//

- (NSMutableArray*)channels
{
#if 1
    return _RSSChannels;
#else
    // Return copied object
    return [[_RSSChannels copy] autorelease];
#endif
}

- (NSMutableArray*)items
{
#if 1
    return _RSSItems;
#else
    // Return copied object
    return [[_RSSItems copy] autorelease];
#endif
}

+ (NSMutableDictionary*)channelWithDocumentIdentifier:(NSString*)identifier channels:(NSArray*)channels
{
    NSEnumerator*           enumerator;
    NSMutableDictionary*    channel;
    enumerator = [channels objectEnumerator];
    while (channel = [enumerator nextObject]) {
        if ([identifier isEqualToString:[channel objectForKey:@"documentId"]]) {
            return channel;
        }
    }
    
    return nil;
}

- (NSMutableDictionary*)channelWithDocumentIdentifier:(NSString*)identifier
{
    return [[self class] channelWithDocumentIdentifier:identifier channels:_RSSChannels];
}

+ (NSMutableDictionary*)itemWithItemIdentifier:(NSString*)identifier channel:(NSDictionary*)channel
{
    NSEnumerator*           enumerator;
    NSMutableDictionary*    item;
    enumerator = [[channel objectForKey:@"items"] objectEnumerator];
    while (item = [enumerator nextObject]) {
        NSString*   itemId;
        itemId = SRSDRSSItemIdentifier(item);
        if ([identifier isEqualToString:itemId]) {
            return item;
        }
    }
    
    return nil;
}

- (NSMutableDictionary*)itemWithLink:(NSString*)link items:(NSArray*)items
{
    NSEnumerator*           enumerator;
    NSMutableDictionary*    item;
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        if ([link isEqualToString:[item objectForKey:@"link"]]) {
            return item;
        }
    }
    
    return nil;
}

- (void)makeItemsPreviewed:(NSArray*)items
{
    // Make items previewed
    NSEnumerator*           enumerator;
    NSMutableDictionary*    item;
    BOOL                    isUpdated = NO;
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        NSNumber*   isPreviewed;
        isPreviewed = [item objectForKey:@"isPreviewed"];
        if (!isPreviewed || ![isPreviewed boolValue]) {
            [item setObject:[NSNumber numberWithBool:YES] forKey:@"isPreviewed"];
            isUpdated = YES;
        }
    }
    
    // Notify it
    if (isUpdated) {
        [[NSNotificationCenter defaultCenter] 
                postNotificationName:SRRSSItemsPreviewed object:items];
    }
}

- (NSMutableArray*)notUpdateFeeds
{
    return _notUpdateFeeds;
}

- (NSMutableDictionary*)channelWithDocumentId:(NSString*)documentId
{
    // Get from cache
    NSMutableDictionary*    channel;
    channel = [_cachedChannelDict objectForKey:documentId];
    if (channel) {
        return channel;
    }
    
    // Get plist file path
    NSString*   path;
    path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    path = [path stringByAppendingPathComponent:@"Shiira/Feeds"];
    path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%u.plist", [documentId hash]]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        return nil;
    }
    
    // Get channel
    NSData* data;
    data = [NSData dataWithContentsOfFile:path];
    channel = [NSPropertyListSerialization propertyListFromData:data 
            mutabilityOption:NSPropertyListMutableContainersAndLeaves format:NULL errorDescription:NULL];
    if (!channel) {
        return nil;
    }
    
    [_cachedChannelDict setObject:channel forKey:@"documentId"];
    return channel;
}

- (NSMutableDictionary*)itemWithItemId:(NSString*)itemId documentId:(NSString*)documentId
{
    // Get channel
    NSMutableDictionary*    channel;
    channel = [self channelWithDocumentId:documentId];
    if (!channel) {
        return nil;
    }
    
    // Find item
    NSEnumerator*           enumerator;
    NSMutableDictionary*    item;
    enumerator = [[channel objectForKey:@"items"] objectEnumerator];
    while (item = [enumerator nextObject]) {
        if ([itemId isEqualToString:SRSDRSSItemIdentifier(item)]) {
            return item;
        }
    }
    
    return nil;
}

+ (int)numberOfNotPreviewedImtesWithChannel:(NSDictionary*)channel
{
    int count = 0;
    
    NSEnumerator*   enumerator;
    NSDictionary*   item;
    enumerator = [[channel objectForKey:@"items"] objectEnumerator];
    while (item = [enumerator nextObject]) {
        NSNumber*   number;
        number = [item objectForKey:@"isPreviewed"];
        if (!number || ![number boolValue]) {
            count++;
        }
    }
    
    return count;
}

- (int)numberOfNotPreviewedItems
{
    int count = 0;
    
    NSEnumerator*   enumerator;
    NSDictionary*   item;
    enumerator = [_RSSItems objectEnumerator];
    while (item = [enumerator nextObject]) {
        NSNumber*   number;
        number = [item objectForKey:@"isPreviewed"];
        if (!number || ![number boolValue]) {
            count++;
        }
    }
    
    return count;
}

- (int)numberOfNewComingArticles
{
    return _newComingArticles;
}

//--------------------------------------------------------------//
#pragma mark -- Persistence --
//--------------------------------------------------------------//

- (void)reloadFeeds
{
    // Load Feeds.plist
    NSString*       path;
    NSMutableArray* array;
    path = [[self class] feedsFilePath];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        return;
    }
    array = [NSPropertyListSerialization propertyListFromData:[NSData dataWithContentsOfFile:path] 
            mutabilityOption:NSPropertyListMutableContainersAndLeaves 
            format:NULL 
            errorDescription:NULL];
    if (!array) {
        return;
    }
    
    // Remove old articles
    
    // Transcript preview info
    NSEnumerator*   enumerator;
    NSDictionary*   channel;
    enumerator = [array objectEnumerator];
    while (channel = [enumerator nextObject]) {
        NSDictionary*   currentChannel;
        currentChannel = [self channelWithDocumentIdentifier:[channel objectForKey:@"documentId"]];
        if (currentChannel) {
            NSArray*    currentItems;
            currentItems = [currentChannel objectForKey:@"items"];
            
            NSEnumerator*           itemEnumerator;
            NSMutableDictionary*    item;
            itemEnumerator = [[channel objectForKey:@"items"] objectEnumerator];
            while (item = [itemEnumerator nextObject]) {
                NSString*   itemLink;
                itemLink = [item objectForKey:@"link"];
                if (itemLink) {
                    NSDictionary*   currentItem;
                    currentItem = [self itemWithLink:itemLink items:currentItems];
                    if (currentItem) {
                        // Set preview info
                        NSNumber*   preview;
                        preview = [currentItem objectForKey:@"isPreviewed"];
                        if (preview) {
                            [item setObject:preview forKey:@"isPreviewed"];
                        }
                    }
                }
            }
        }
    }
    
    // Remove old channles and items
#if 1
    [_RSSChannels release];
    _RSSChannels = [[NSMutableArray array] retain];
    [_RSSItems release];
    _RSSItems = [[NSMutableArray array] retain];
#else
    [_RSSChannels removeAllObjects];
    [_RSSItems removeAllObjects];
#endif
    
    // Copy channels
    enumerator = [array objectEnumerator];
    while (channel = [enumerator nextObject]) {
        // Get bookmark for this channel
        NSString*   feedURLString;
        NSArray*    bookmarks;
        feedURLString = [channel objectForKey:@"documentId"];
        bookmarks = [[SRBookmarkStorage sharedInstance] findBookmarkWithURLString:feedURLString];
        if (!bookmarks || [bookmarks count] == 0) {
            continue;
        }
        
        // Check auto update flag
        NSEnumerator*   bookmarkEnumerator;
        SRBookmark*     bookmark;
        bookmarkEnumerator = [bookmarks objectEnumerator];
        while (bookmark = [bookmarkEnumerator nextObject]) {
            if ([bookmark isAutoUpdate]) {
                [_RSSChannels addObject:channel];
                
                // Copy items
                [_RSSItems addObjectsFromArray:[channel objectForKey:@"items"]];
                break;
            }
        }
    }
}

- (void)saveFeeds
{
    // Get Feeds.plist path
    NSString*       path;
    path = [[self class] feedsFilePath];
    
    // Copy channels
    NSMutableArray*         channels;
    NSEnumerator*           enumerator;
    NSMutableDictionary*    channel;
    channels = [NSMutableArray array];
    enumerator = [_RSSChannels objectEnumerator];
    while (channel = [enumerator nextObject]) {
        // Copy channel
        NSMutableDictionary*    copiedChannel;
        copiedChannel = [NSMutableDictionary dictionaryWithDictionary:channel];
        
        // Remove document
        if ([copiedChannel objectForKey:@"RSSDocument"]) {
            [copiedChannel removeObjectForKey:@"RSSDocument"];
        }
        
        // Add channel
        [channels addObject:copiedChannel];
    }
    
    // Save channels
    if (![channels writeToFile:path atomically:YES]) {
        // Error
        NSLog(@"Failed to save Feeds.plist");
    }
}

- (void)loadNotUpdateFeeds
{
    // Load NotUpdaateFeeds.plist
    NSString*       path;
    NSMutableArray* array;
    path = [[self class] notUpdateFeedsFilePath];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        return;
    }
    array = [NSArray arrayWithContentsOfFile:path];
    if (!array) {
        return;
    }
    
    // Copy feeds
    [_notUpdateFeeds removeAllObjects];
    [_notUpdateFeeds addObjectsFromArray:array];
    
    // Update bookmark
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    enumerator = [[[SRBookmarkStorage sharedInstance] RSSBookmarks] objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        NSString*   URLString;
        URLString = [bookmark URLString];
        if ([_notUpdateFeeds containsObject:URLString]) {
            [bookmark setAutoUpdate:NO];
        }
    }
}

- (void)seveNotUpdateFeeds
{
    // Get NotUpdaateFeeds.plist path
    NSString*       path;
    path = [[self class] notUpdateFeedsFilePath];
    
    // Save not update feeds
    if (![_notUpdateFeeds writeToFile:path atomically:YES]) {
        // Error
        NSLog(@"Failed to save NotUpdaateFeeds.plist");
    }
}

//--------------------------------------------------------------//
#pragma mark -- RSS auto update --
//--------------------------------------------------------------//

- (void)restartUpdateTimer
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Cancel update timer
    if (_updateTimer) {
        [_updateTimer invalidate];
        _updateTimer = nil;
    }
    
    // Decide timer interval
    NSTimeInterval  interval;
    int             update, minutes;
    update = [defaults integerForKey:SRRSSUpdate];
    if (update == SRRSSUpdateEverySpecifiedMinutes) {
        minutes = [defaults integerForKey:SRRSSUpdateMinutes];
        if (minutes <= 0 || minutes >= 60) {
            minutes = 30;
        }
    }
    
    switch (update) {
    case SRRSSUpdateEverySpecifiedMinutes: {
        interval = minutes * 60;
        break;
    }
    case SRRSSUpdateEveryHour: {
        interval = 60 * 60;
        break;
    }
    case SRRSSUpdateEveryDay: {
        interval = 60 * 60 * 24;
        break;
    }
    case SRRSSUpdateNever: {
        // Do not restart timer
        return;
    }
    }
    
    // Restart timer
    _updateTimer = [NSTimer scheduledTimerWithTimeInterval:interval 
            target:self 
            selector:@selector(updateTimerFired:) 
            userInfo:NULL 
            repeats:NO];
}

- (void)cancelUpdateTimer
{
    // Cancel update timer
    if (_updateTimer) {
        [_updateTimer invalidate];
        _updateTimer = nil;
    }
}

- (void)updateTimerFired:(NSNotification*)notification
{
    // Timer is invalidated
    _updateTimer = nil;
    
    // Refresh feeds
    [self refreshAllFeeds];
}

//--------------------------------------------------------------//
#pragma mark -- NSTask notification --
//--------------------------------------------------------------//

- (void)taskDidTerminate:(NSNotification*)notification
{
    // Release task
    if (_RSSTask && [notification object] == _RSSTask) {
        [_RSSTask release];
        _RSSTask = nil;
    }
    
    _isSyndicationWorking = NO;
}

//--------------------------------------------------------------//
#pragma mark -- SRBookmarkStorage notification --
//--------------------------------------------------------------//

- (void)bookmarkAdded:(NSNotification*)notification
{
    // Get bookmarks
    NSArray*    bookmarks;
    bookmarks = [notification object];
    if (!bookmarks || ![bookmarks isKindOfClass:[NSArray class]]) {
        return;
    }
    
    // Get RSS feeds
    NSMutableArray* feeds;
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    feeds = [NSMutableArray array];
    enumerator = [bookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        if ([bookmark type] == SRBookmarkTypeRSS) {
            [feeds addObject:[bookmark URLString]];
        }
    }
    if ([feeds count] == 0) {
        return;
    }
    
    // Refresh feeds
    [self refreshFeeds:feeds];
}

- (void)bookmarkRemoved:(NSNotification*)notification
{
    // Get bookmarks
    NSArray*    bookmarks;
    bookmarks = [notification object];
    if (!bookmarks || ![bookmarks isKindOfClass:[NSArray class]]) {
        return;
    }
    
    // Get RSS feeds
    NSMutableArray* feeds;
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    feeds = [NSMutableArray array];
    enumerator = [bookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        if ([bookmark type] == SRBookmarkTypeRSS) {
            [feeds addObject:[bookmark URLString]];
        }
    }
    if ([feeds count] == 0) {
        return;
    }
    
}

- (void)bookmarkChanged:(NSNotification*)notification
{
}

- (void)bookmarkRSSAutoUpdateChanged:(NSNotification*)notification
{
    // Get bookmark
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    enumerator = [[notification object] objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        NSString*   feedURLString;
        feedURLString = [bookmark URLString];
        if (!feedURLString) {
            return;
        }
        
        // For turning on
        if ([bookmark isAutoUpdate]) {
            // Remvoe this feed from array
            if (![bookmark isMutable]) {
                if ([_notUpdateFeeds containsObject:feedURLString]) {
                    [_notUpdateFeeds removeObject:feedURLString];
                }
            }
        }
        // For turning off
        else {
            // Add this feed to array
            if (![bookmark isMutable]) {
                if (![_notUpdateFeeds containsObject:feedURLString]) {
                    [_notUpdateFeeds addObject:feedURLString];
                }
            }
            
            // Find this feed from channels
            NSEnumerator*   enumerator;
            NSDictionary*   channel;
            enumerator = [_RSSChannels objectEnumerator];
            while (channel = [enumerator nextObject]) {
                // Check feed URL
                NSString*   documentId;
                documentId = [channel objectForKey:@"documentId"];
                if ([feedURLString isEqualToURLStringWithoutScheme:documentId]) {
                    break;
                }
            }
            
            if (channel) {
                // Remove items
                NSDictionary*   item;
                enumerator = [[channel objectForKey:@"items"] objectEnumerator];
                while (item = [enumerator nextObject]) {
                    [_RSSItems removeObject:item];
                }
                
                // Remove channel
                [_RSSChannels removeObject:channel];
            }
            
            // Notify change
            [[NSNotificationCenter defaultCenter] 
                    postNotificationName:SRRSSFeedsDidChanged object:self];
        }
    }
}

//--------------------------------------------------------------//
#pragma mark -- RSS syndication notification --
//--------------------------------------------------------------//

- (void)sdFeedWillStartRefresh:(NSNotification*)notification
{
    _isSyndicationWorking = YES;
    
    // Notify refresh
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRRSSWillStartRefresh object:self userInfo:[notification userInfo]];
}

- (void)sdFeedProgressRefresh:(NSNotification*)notification
{
    // Realod data
    [self reloadFeeds];
    
    // Icnrease number of new coming articles
    _newComingArticles += [[[notification userInfo] objectForKey:@"numberOfNewItems"] intValue];
    
    // Notify refresh
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRRSSProgressRefresh object:self userInfo:[notification userInfo]];
}

- (void)sdFeedDidEndRefresh:(NSNotification*)notification
{
    _isSyndicationWorking = NO;
    
    // Notify refresh
    [[NSNotificationCenter defaultCenter] 
            postNotificationName:SRRSSDidEndRefresh object:self userInfo:[notification userInfo]];
    
    // Restart timer
    [self restartUpdateTimer];
}

//--------------------------------------------------------------//
#pragma mark -- Key value observation --
//--------------------------------------------------------------//

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For NSUserDefault
    if (object == [NSUserDefaults standardUserDefaults]) {
        // RSS preferences
        if ([keyPath isEqualToString:SRRSSUpdate]) {
            // Restart timer
            [self restartUpdateTimer];
            return;
        }
    }
}

@end
