//
//  KMBrowser.m
//  BathyScaphe
//
//  Created by Hori,Masaki on 11/07/18.
//  Copyright 2011 masakih. All rights reserved.
//

#import "KMBrowser.h"

#import "KMDocument.h"
#import "KMBrowserDocumentProxy.h"

#import "KMBoardList.h"
#import "KMThreadList.h"
#import "KMBSLogViewController.h"
#import "KMStatusLineViewController.h"

#import "KMLogWindowController.h"

#import "CMRThreadAttributes.h"
#import "AppDefaults.h"

// Localized
#define kSearchListNotFoundKey				@"Search Thread Not Found"
#define kSearchListResultKey				@"Search Thread Result"

@interface KMBrowser(KMViewAccessor)
- (void)buildViews;
@end

@implementation KMBrowser

@synthesize boardListPlaceholder;
@synthesize rightView;
@synthesize threadListPlaceholder;
@synthesize progressViewPlaceholder;

@synthesize bsviewPlaceholder;

@synthesize searchField = _searchField;


@synthesize useLogWindow = _useLogWindow;


- (id)init
{
	self = [super initWithWindowNibName:@"KMBrowser"];
	if(self) {
		_docProxy = [[KMBrowserDocumentProxy alloc] init];
		_docProxy.browser = self;
	}
	return self;
}

- (void)dealloc
{
	[progressView release];
	
	[boardList removeObserver:self forKeyPath:@"selection"];
	[boardList release];
	
	[threadList removeObserver:self forKeyPath:@"numberOfFilteredThreads"];
	[threadList release];
	
	[self.searchField unbind:NSValueBinding];
	
	[bsview release];
	
	[_associatedResponders release];
	[_recentAssociatedResponders release];
	[_docProxy release];
	
	[super dealloc];
}

- (void)windowDidLoad
{
	[super windowDidLoad];
	[self buildViews];
	[self.window setContentBorderThickness:22 forEdge:NSMinYEdge];
	_associatedResponders = [[NSArray alloc] initWithObjects:
							bsview, threadList, boardList, progressView, nil];
}

+ (NSSet *)keyPathsForValuesAffectingBoardName
{
	return [NSSet setWithObjects:@"doc", @"window.firstResponder", @"boardList.selection", nil];
}
- (NSString *)boardName
{
	NSResponder *responder = [[self window] firstResponder];
	while(responder) {
		if(responder == bsview ) {
			return [super boardName];
		}
		responder = [responder nextResponder];
	}
	return [boardList.selection representName];
}

- (void)setDocument:(NSDocument *)document
{
	if([self.document windowControllers].count == 1) {
		[self.document performSelector:@selector(close)
							withObject:nil
							afterDelay:0.0001];
	}
	[super setDocument:document];
}
- (void)windowWillClose:(NSNotification *)notification
{
	if(self.document) [self saveDocumentViewAttributes];
	[self.doc removeWindowController:self];
}
- (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName
{
    if (!threadList || !threadList.boardName) {
		id mainBundle = [NSBundle mainBundle];
		id p = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"];
		if(p) return p;
		return @"Browser";
	}
    NSString *foo;
	
    if (threadList.filterString) {
        NSUInteger foundNum = [threadList numberOfFilteredThreads];
		
        if (0 == foundNum) {
            foo = NSLocalizedStringFromTable(kSearchListNotFoundKey, @"ThreadsList", @"");
        } else {
			// #warning 64BIT: Check formatting arguments
			// 2010-07-29 tsawada2 修正済
            foo = [NSString stringWithFormat:NSLocalizedStringFromTable(kSearchListResultKey, @"ThreadsList", @""), (unsigned long)foundNum];
        }
    } else {
        NSString *base_ = [threadList viewMode] ? NSLocalizedStringFromTable(@"Browser Title (Log Mode)", @"ThreadsList", @"")
		: NSLocalizedStringFromTable(@"Browser Title (Thread Mode)", @"ThreadsList", @"");
		// #warning 64BIT: Check formatting arguments
		// 2010-07-29 tsawada2 修正済
        foo = [NSString stringWithFormat:base_, (unsigned long)[threadList numberOfFilteredThreads]];
    }
	
    return [NSString stringWithFormat:@"%@ (%@)", [threadList boardName], foo];
}
- (void)synchronizeWindowTitle
{
	[self.window setTitle:[self windowTitleForDocumentDisplayName:nil]];
}

static BOOL documentHasWindowController(NSDocument *aDocument, Class aClass)
{
	NSArray *windowControllers = [aDocument windowControllers];
	for(NSWindowController *wc in windowControllers) {
		if([wc isKindOfClass:aClass]) return YES;
	}
	return NO;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	if(context == boardList) {
		if([keyPath isEqualToString:@"selection"]) {
//			NSLog(@"Board list did change selection.");
			[threadList setRepresentedObject:boardList.selection];
			[self synchronizeWindowTitle];
			[self.window makeFirstResponder:threadList.view];
		}
		
		return;
	}
	if(context == threadList) {
		if([keyPath isEqualToString:@"numberOfFilteredThreads"]) {
			[self synchronizeWindowTitle];
		}
		return;
	}
	
	return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}

- (void)reselectThread:(id)dummy
{
	if(self.doc.threadAttr) {
		[threadList selectThreadWithAttributes:[NSArray arrayWithObject:self.doc.threadAttr] scrollToVisible:[CMRPref threadsListAutoscrollMask]];
	}
}
- (id)logViewController
{
	return bsview;
}

@end

@implementation KMBrowser (EventTracking)

- (void)swipeWithEvent:(NSEvent *)event
{
	NSInteger dx = [event deltaX];
	
	if(dx < 0) {
		[self showBoardList:self];
	} else if(dx > 0) {
		[self hideBoardList:self];
	} else if([event deltaY] > 0) {
		[self hideThreadList:self];
	} else {
		[self showThreadList:self];
	}
}
@end

@implementation KMBrowser (Actions)
- (void)didEndVAnimation:(id)timer
{
	vAnimation = NO;
}
- (void)didEndHAnimation:(id)timer
{
	hAnimation = NO;
}
- (BOOL)isShowsThreadList
{
	CGFloat threadBottomEdge = [threadList.view frame].origin.y;
	CGFloat rightHeight = [rightView frame].size.height;
	CGFloat delta = rightHeight - threadBottomEdge;
	return (delta > 2.0 || delta < -2.0);
}
- (IBAction)showThreadList:(id)sender
{
	if([self isShowsThreadList]) return;
	if(vAnimation) return;
	vAnimation = YES;
	
	NSRect frame = [threadList.view frame];
	CGFloat threadListHeight = frame.size.height;
	frame.origin.y -= threadListHeight;
	[[threadList.view animator] setFrame:frame];
	
	frame = [bsview.view frame];
	frame.size.height -= threadListHeight;
	[[bsview.view animator] setFrame:frame];
	
	[NSTimer scheduledTimerWithTimeInterval:[[NSAnimationContext currentContext] duration]
									 target:self
								   selector:@selector(didEndVAnimation:)
								   userInfo:nil
									repeats:NO];
}
- (IBAction)hideThreadList:(id)sender
{
	if(![self isShowsThreadList]) return;
	if(vAnimation) return;
	vAnimation = YES;
	
	NSRect frame = [threadList.view frame];
	CGFloat threadListHeight = frame.size.height;
	frame.origin.y += threadListHeight;
	[[threadList.view animator] setFrame:frame];
	
	frame = [bsview.view frame];
	frame.size.height += threadListHeight;
	[[bsview.view animator] setFrame:frame];
	
	[NSTimer scheduledTimerWithTimeInterval:[[NSAnimationContext currentContext] duration]
									 target:self
								   selector:@selector(didEndVAnimation:)
								   userInfo:nil
									repeats:NO];
}
- (IBAction)collapseOrExpandThreadList:(id)sender
{
	if(![self isShowsThreadList]) {
		[self showThreadList:self];
	} else {
		[self hideThreadList:self];
	}
}


- (BOOL)isShowBoardList
{
	CGFloat boardLeftEdge = [boardList.view frame].origin.x;
	return (boardLeftEdge < 2.0 && boardLeftEdge > -2.0);
}
- (IBAction)showBoardList:(id)sender
{
	if([self isShowBoardList]) return;
	if(hAnimation) return;
	hAnimation = YES;
	
	CGFloat baordListWidth = 0;
	NSRect frame = [boardList.view frame];
	baordListWidth = frame.size.width;
	frame.origin.x += baordListWidth;
	[[boardList.view animator] setFrame:frame];
	
	frame = [rightView frame];
	frame.origin.x += baordListWidth;
	frame.size.width -= baordListWidth;
	[[rightView animator] setFrame:frame];
	
	progressView.hasAddBoardButton = YES;
	progressView.leftAreaWidth = [boardList.view frame].size.width;
	
	[NSTimer scheduledTimerWithTimeInterval:[[NSAnimationContext currentContext] duration]
									 target:self
								   selector:@selector(didEndHAnimation:)
								   userInfo:nil
									repeats:NO];
}
- (IBAction)hideBoardList:(id)sender
{
	if(![self isShowBoardList]) return;
	if(hAnimation) return;
	hAnimation = YES;
	
	CGFloat baordListWidth = 0;
	NSRect frame = [boardList.view frame];
	baordListWidth = frame.size.width;
	frame.origin.x -= baordListWidth;
	[[boardList.view animator] setFrame:frame];
	
	frame = [rightView frame];
	frame.origin.x -= baordListWidth;
	frame.size.width += baordListWidth;
	[[rightView animator] setFrame:frame];
	
	progressView.hasAddBoardButton = NO;
	progressView.leftAreaWidth = 0;
	
	[NSTimer scheduledTimerWithTimeInterval:[[NSAnimationContext currentContext] duration]
									 target:self
								   selector:@selector(didEndHAnimation:)
								   userInfo:nil
									repeats:NO];
}

- (IBAction)collapseOrExpandBoardList:(id)sender
{
	if(![self isShowBoardList]) {
		[self showBoardList:self];
	} else {
		[self hideBoardList:self];
	}
}

- (IBAction)openSelectedThreads:(id)sender
{
	if([threadList.selection count] == 1) {
		id selection = [threadList.selection objectAtIndex:0];
		NSString *filepath = [selection path];
		
		id dc = [NSDocumentController sharedDocumentController];
		NSError *error = nil;
		KMDocument *doc = [dc openDocumentWithContentsOfURL:[NSURL fileURLWithPath:filepath]
													display:NO
													  error:&error];
		
		if(!doc && error) {
			NSLog(@"%@", error);
		}
		BOOL hasSingleWindow = documentHasWindowController(doc, [KMLogWindowController class]);
		if(self.useLogWindow && !hasSingleWindow) {
			KMLogWindowController *wc = [[[KMLogWindowController alloc] init] autorelease];
			[doc addWindowController:wc];
		} else if(!self.useLogWindow && ![[doc windowControllers] containsObject:self]) {
			[doc addWindowController:self];
			[self.window makeFirstResponder:bsview.view];
		}
		[doc showWindows];
	} else {
		// Multiple selection.
		NSLog(@"Multiple selection.");
	}
}

- (IBAction)test01:(id)sender
{
	id firstResponder = [[NSApp mainWindow] firstResponder];
	while(firstResponder) {
		fprintf(stderr, "%s -|\n", [[firstResponder description] UTF8String]);
		firstResponder = [firstResponder nextResponder];
	}
}
- (NSArray *)associatedResponder
{
	// 現在のレスポンダーチェーンに含まれるレスポンダーは除外
	if(!_associatedResponders) return nil;
	
	NSResponder *responder = [[self window] firstResponder];
	if(responder != _recentFirstResponder) {
		[_recentAssociatedResponders release];
		_recentAssociatedResponders = [_associatedResponders mutableCopy];
		_recentFirstResponder = responder;
		while(responder) {
			if([_recentAssociatedResponders containsObject:responder]) {
				[_recentAssociatedResponders removeObject:responder];
				if(responder == bsview) {
					[_recentAssociatedResponders insertObject:_docProxy atIndex:0];
				}
				break;
			}
			responder = [responder nextResponder];
		}
	}
	
	return _recentAssociatedResponders;
}

#pragma mark -
#pragma mark -- Menu Validation --
- (BOOL)validateCollapseOrExpandBoardListItem:(id)theItem
{
	if ([theItem isKindOfClass:[NSMenuItem class]]) {
		[theItem setTitle:[self isShowBoardList] ? NSLocalizedString(@"Collapse Boards List", @"") :
		 NSLocalizedString(@"Expand Boards List", @"")];
	}
	return YES;
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
	SEL action = [menuItem action];
	
	if(action == @selector(collapseOrExpandBoardList:)) {
		return [self validateCollapseOrExpandBoardListItem:menuItem];
	}
	if([super respondsToSelector:action]) {
		return [super validateMenuItem:menuItem];
	}
	
	for(id responder in [self associatedResponder]) {
		if([responder respondsToSelector:action]) {
			if([responder respondsToSelector:_cmd]) {
				return [responder validateMenuItem:menuItem];
			}
			if([responder respondsToSelector:@selector(validateUserInterfaceItem:)]) {
				return [responder validateUserInterfaceItem:menuItem];
			}
		}
	}
	
	return YES;
}
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
{
	if([(id)item isKindOfClass:[NSMenuItem class]]) {
		return [self validateMenuItem:(NSMenuItem *)item];
	}
	
	SEL action = [item action];
	for(id responder in [self associatedResponder]) {
		if([responder respondsToSelector:action]) {
			if([responder respondsToSelector:_cmd]) {
				return [responder validateUserInterfaceItem:item];
			}
		}
	}
	
	return NO;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
	for(id responder in [self associatedResponder]) {
		if([responder respondsToSelector:aSelector]) return YES;
	}
	
	return [super respondsToSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
	for(id responder in [self associatedResponder]) {
		if([responder respondsToSelector:aSelector]) {
			return [responder methodSignatureForSelector:aSelector];
		}
	}
	return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
	SEL selector = [anInvocation selector];
	for(id responder in [self associatedResponder]) {
		if([responder respondsToSelector:selector]) {
			[anInvocation invokeWithTarget:responder];
			return;
		}
	}
	[super forwardInvocation:anInvocation];
}


@end

@implementation KMBrowser(KMViewAccessor)

- (void)buildViews
{
	boardList = [[KMBoardList alloc] init];
	[self replaceAndFit:[boardList view] with:boardListPlaceholder];
	self.boardListPlaceholder = nil;
	[boardList addObserver:self
				forKeyPath:@"selection"
				   options:NSKeyValueObservingOptionNew
				   context:boardList];
	
	threadList = [[KMThreadList alloc] init];
	[self replaceAndFit:[threadList view] with:threadListPlaceholder];
	self.threadListPlaceholder = nil;
	[threadList addObserver:self
				 forKeyPath:@"numberOfFilteredThreads"
					options:NSKeyValueObservingOptionNew
					context:threadList];
	[self.searchField bind:NSValueBinding
				  toObject:threadList
			   withKeyPath:@"filterString"
				   options:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(reselectThread:)
												 name:KMThreadListDidUpdateNotification
											   object:threadList];
	
//	logView = [[KMHTMLLogViewController alloc] init];
//	[self replaceAndFit:[logView view] with:logViewPlaceholder];
//	self.logViewPlaceholder = nil;
	
	bsview = [[KMBSLogViewController alloc] init];
	bsview.hasTitleRuler = YES;
	[self replaceAndFit:[bsview view] with:bsviewPlaceholder];
	self.bsviewPlaceholder = nil;
	
	progressView = [[KMStatusLineViewController alloc] init];
	[self replaceAndFit:[progressView view] with:progressViewPlaceholder];
	self.progressViewPlaceholder = nil;
	progressView.windowController = self;
	progressView.hasAddBoardButton = YES;
	progressView.leftAreaWidth = [boardList.view frame].size.width;
	
	bsview.statusLine = progressView;
	[progressView validateIdxNavLazily:bsview];
	
	[self synchronizeWindowTitle];
}
@end
