//
//  EditableObjectContainer.m
//
//

#import "EditableObjectContainer.h"
#import "FileURLHelper.h"

@interface EditableObjectContainer ()
@property (nonatomic, weak) NSTimer * saveInterval;
- (void)autosave;
@end


// Storing a weak reference for the NSTimer to avoid a strong reference cycle
@interface EditableObjectContainerTimerCallback : NSObject
@property (nonatomic, weak) EditableObjectContainer * container;
- (void)callback;
@end

@implementation EditableObjectContainerTimerCallback
- (void)callback { [self.container autosave]; }
@end



@implementation EditableObjectContainer {
	id _contents;
	id _initialContents;
	BOOL _isLocked;
	NSData * _lastSavedData;
}

- (void)dealloc {
	if (self.shouldSaveOnDevice) [self save];
	[NSNotificationCenter.defaultCenter removeObserver:self];
	if (_saveInterval) [_saveInterval invalidate];
}

- (void)awakeFromNib {
	[super awakeFromNib];
	_isLocked = YES;

	if (self.shouldSaveOnDevice) {
		NSData * data = [NSData dataWithContentsOfURL:self.fileURL];
		if (data.length) {
			_contents = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves | NSJSONReadingMutableContainers error:nil];
		}

		// Auto-save every second
		EditableObjectContainerTimerCallback * cb = [EditableObjectContainerTimerCallback new];
		cb.container = self;
		_saveInterval = [NSTimer scheduledTimerWithTimeInterval:2
														 target:cb
													   selector:@selector(callback)
													   userInfo:nil
														repeats:YES];
		
		// Listen for notifications
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(didEnterBackground:)
													 name:UIApplicationDidEnterBackgroundNotification
												   object:nil];
	}

	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
		if (self.initialContentsDataSource) {
			_contents = [self.initialContentsDataSource.contents mutableCopy];
			[self notifyObservers];
		}
		_isLocked = NO;
	});
}

- (void)didEnterBackground:(NSNotification *)note {
	[self save];
}


- (void)autosave {
	if (_contents == nil) return;
	NSData * data = [NSJSONSerialization dataWithJSONObject:_contents options:0 error:nil];
	if ([data isEqualToData:_lastSavedData]) {
		return;
	}
	_lastSavedData = data;
	[self save];
}

- (void)save {
	[self saveOnDevice];
}

- (void)saveOnDevice {
	if (_contents == nil) return;
	NSData * data = [NSJSONSerialization dataWithJSONObject:_contents options:NSJSONWritingPrettyPrinted error:nil];
	[data writeToURL:self.fileURL atomically:YES];
}

- (NSURL *)fileURL {
	return GetFileURLForDocumentWithID(self.identifier, @"json", nil);
}

- (void)replaceContentsWithDictionary:(NSDictionary *)dict {
	if (![dict isKindOfClass:[NSDictionary class]]) return;
	_contents = ((NSDictionary *)dict).mutableCopy;
	[self notifyObservers];
}

- (void)setDataString:(NSString *)dataString {
	_contents = [NSJSONSerialization JSONObjectWithData:[dataString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableLeaves|NSJSONReadingMutableContainers error:nil];
	if ([_contents isKindOfClass:[NSArray class]] || [_contents isKindOfClass:[NSDictionary class]]) {
		_initialContents = ((NSObject *)_contents).copy;
	}
}

- (BOOL)didLoad {
	return YES;
}

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath {
	if ([keyPath hasPrefix:@"contents."]) {
		if (_isLocked) return;
		if (!_contents) _contents = @{}.mutableCopy;
		[_contents setValue:value forKeyPath:[keyPath substringFromIndex:9]];
		[self notifyObserversWithUpdate:YES];
		return;
	}
	[super setValue:value forKey:keyPath];
}

- (void)resetContents {
	if ([_contents isKindOfClass:[NSArray class]] || [_contents isKindOfClass:[NSDictionary class]]) {
		_contents = [_initialContents mutableCopy];
	}
	[self notifyObservers];
}

- (void)clearContents {
	_contents = @{}.mutableCopy;
	[self notifyObservers];
}

#pragma mark - DataSourceBase methods

- (id)contents {
	return _contents;
}

- (NSInteger)numberOfSections {
	return 1;
}

- (NSInteger)numberOfItemsInSection:(NSInteger)section {
	if ([_contents isKindOfClass:[NSArray class]]) {
		return [_contents count];
	}
	return 0;
}

- (NSDictionary *)datasetForItemAtIndexPath:(NSIndexPath *)indexPath {
	if ([_contents isKindOfClass:[NSArray class]]) {
		return [_contents objectAtIndex:indexPath.row];
	}
	return nil;
}

@end
