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

#import "KMBoardList.h"

#import "AppDefaults.h"
#import <SGAppKit/SGAppKit.h>

#import "BoardManager.h"
#import "CMRHistoryManager.h"
#import "KMBoardListItemCell.h"

#import "AddBoardSheetController.h"
#import "BSBoardInfoInspector.h"

#import "CMRThreadSignature.h"


#define APP_BROWSER_THREADSLIST_TABLE_AUTOSAVE_NAME	@"CocoMonar:ThreadsListTable Autosave"

#define APP_TVIEW_LOCALIZABLE_FILE			@"ThreadViewer"

@interface NSObject (KM_NSTableView_Lion)
- (void)setRowSizeStyle:(NSInteger)style;
@end

@interface KMBoardList()
- (void)updateBoardListViewWithNeedingDisplay:(BOOL)display;
@end

@interface KMBoardList (ListEditor) <EditBoardSheetControllerDelegate>
- (BOOL)reselectBoard:(id)boardListItem;
@end

@implementation KMBoardList
@synthesize boardListView;
@synthesize selection;
@synthesize editBoardSheetController = _editBoardSheetController;
@synthesize addBoardSheetController = _addBoardSheetController;

- (id)init
{
	self = [super initWithNibName:@"KMBoardList" bundle:nil];
	if(self) {
		SmartBoardList *userList = [[BoardManager defaultManager] userList];
		[self setRepresentedObject:userList];
		
		NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
		[nc addObserver:self
			   selector:@selector(boardManagerUserListDidChange:)
				   name:CMRBBSManagerUserListDidChangeNotification
				 object:[BoardManager defaultManager]];
		[nc addObserver:self
			   selector:@selector(appDefaultsLayoutSettingsUpdated:)
				   name:AppDefaultsLayoutSettingsUpdatedNotification
				 object:CMRPref];
	}
	return self;
}
- (void)dealloc
{
	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
	[nc removeObserver:self];
	
	[_editBoardSheetController release];
	[_addBoardSheetController release];
	
	[super dealloc];
}

- (void)awakeFromNib
{
	NSTableColumn    *column_;
	BSIconAndTextCell	*cell_;
	
	column_ = [boardListView tableColumnWithIdentifier:BoardPlistNameKey];
	cell_ = [[KMBoardListItemCell alloc] init];
	[cell_ setEditable:NO];
	[column_ setDataCell:cell_];
	[cell_ release];
	[column_ setEditable:NO];
	
	[boardListView setDataSource:[self representedObject]];
	
	[boardListView registerForDraggedTypes:[NSArray arrayWithObjects:BSPasteboardTypeBoardListItem, BSPasteboardTypeThreadSignature, nil]];
	[boardListView setDraggingSourceOperationMask:(NSDragOperationCopy | NSDragOperationGeneric | NSDragOperationMove) forLocal:YES];
	[boardListView setDraggingSourceOperationMask:NSDragOperationGeneric forLocal:NO];
	
	[boardListView setAutosaveName:APP_BROWSER_THREADSLIST_TABLE_AUTOSAVE_NAME];
	[boardListView setAutosaveExpandedItems:YES];
	[boardListView setDoubleAction:@selector(boardListViewDoubleAction:)];
	
	[boardListView setAutoresizesOutlineColumn:NO];
	[boardListView setIndentationMarkerFollowsCell:YES];
	
	[self updateBoardListViewWithNeedingDisplay:NO];
	
	[self performSelector:@selector(reselectBoardInWake:)
			   withObject:nil
			   afterDelay:0.0];
}

static NSInteger expandAndSelectItem(BoardListItem *selected, NSArray *anArray, NSOutlineView *bLT)
{
	NSEnumerator *iter_ = [anArray objectEnumerator];
	id	eachItem;
	NSInteger index = -1;
	while (eachItem = [iter_ nextObject]) {
		// 「閉じているカテゴリ」だけに興味がある
		if (NO == [SmartBoardList isCategory: eachItem] || NO == [eachItem hasChildren]) continue;
		
		if (NO == [bLT isItemExpanded: eachItem]) [bLT expandItem: eachItem];
		
		index = [bLT rowForItem: selected];
		if (-1 != index) { // 当たり！
			return index;
		} else { // カテゴリ内のサブカテゴリを開いて検査する
			index = expandAndSelectItem(selected, [eachItem items], bLT);
			if (-1 == index) // このカテゴリのどのサブカテゴリにも見つからなかった
				[bLT collapseItem: eachItem]; // このカテゴリは閉じる
		}
	}
	return index;
}

- (NSInteger)searchRowForItemInDeep:(BoardListItem *)boardItem inView:(NSOutlineView *)olView
{
	NSInteger	index = [olView rowForItem:boardItem];
	
	if (index == -1) {
		index = expandAndSelectItem(boardItem, [(SmartBoardList *)[olView dataSource] boardItems], olView);
	}
	
	return index;
}
- (void)selectRowOfName:(NSString *)boardName
{
	if(!boardName) return;
	
	SmartBoardList  *dataSource = [boardListView dataSource];
	BoardListItem *selectedItem = [dataSource itemForName:boardName];
	if(!selectedItem) {
		SmartBoardList	*defaultList = [[BoardManager defaultManager] defaultList];
		BoardListItem	*newItem = [defaultList itemForName:boardName];
		if (!newItem) {
			NSBeep();
			NSLog(@"No BoardListItem for board %@ found.", boardName);
			return;
		} else {
			[dataSource addItem:newItem afterObject:nil];
			selectedItem = [dataSource itemForName:boardName];
		}
		return;
	}
	
	NSInteger index = [self searchRowForItemInDeep:selectedItem inView:boardListView];	
	if (index == -1) return;
	[boardListView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
	[boardListView scrollRowToVisible:index];
}

- (void)reselectBoardInWake:(id)dummy
{
	NSString *lastBoard = [CMRPref browserLastBoard];
	if(!lastBoard) return;
	[self selectRowOfName:lastBoard];
}

- (void)updateBoardListViewWithNeedingDisplay:(BOOL)display
{
    NSInteger rowSizeStyle = [CMRPref boardListRowSizeStyle];
    if ([boardListView respondsToSelector:@selector(setRowSizeStyle:)]) { // Lion
        [boardListView setRowSizeStyle:rowSizeStyle];
    } else { // 10.6
        CGFloat height = 20;
        if (rowSizeStyle == 3) {
            height = 34;
        } else if (rowSizeStyle == 2) {
            height = 24;
        }
        [boardListView setRowHeight:height];
    }
	
	if (display) {
		[boardListView setNeedsDisplay:display];
	}
}

- (void)boardManagerUserListDidChange:(NSNotification *)notification
{
	UTILAssertNotificationName(notification, CMRBBSManagerUserListDidChangeNotification);
	UTILAssertNotificationObject(notification, [BoardManager defaultManager]);
	
	[self.boardListView reloadData];
	[self reselectBoard:self.selection];
}
- (void)appDefaultsLayoutSettingsUpdated:(NSNotification *)notification
{
	UTILAssertNotificationName(notification, AppDefaultsLayoutSettingsUpdatedNotification);
	UTILAssertNotificationObject(notification, CMRPref);
	
	[self updateBoardListViewWithNeedingDisplay:YES];
}

#pragma mark NSOutlineView Delegate
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
	NSInteger	rowIndex_;
	UTILAssertNotificationName(
							   notification,
							   NSOutlineViewSelectionDidChangeNotification);
	UTILAssertNotificationObject(
								 notification,
								 boardListView);
	
	rowIndex_ = [boardListView selectedRow];
	
	if ([boardListView numberOfSelectedRows] > 1) {
		[NSException raise:@"KMBoardListException" format:@"multi selection index."];
	}
	if (rowIndex_ < 0) {
		return;
	}
	if (rowIndex_ >= [boardListView numberOfRows]) {
		[NSException raise:@"KMBoardListException" format:@"overflow selection index."];
	}
	
	self.selection = [boardListView itemAtRow:rowIndex_];
	NSString *name = [self.selection representName];
	// 読み込みの完了、設定に保存
	// 履歴に登録してから、変更の通知
	[CMRPref setBrowserLastBoard:name];
	[[CMRHistoryManager defaultManager] addItemWithTitle:name type:CMRHistoryBoardEntryType object:self.selection];
}
- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    if ([[tableColumn identifier] isEqualToString:BoardPlistNameKey]) {
        NSImage *image;
        if (![CMRPref boardListShowsIcon]) {
            image = nil;
        } else {
            NSInteger style = [CMRPref boardListRowSizeStyle];
            NSString *iconName = [item iconBaseName];
            if (style == 2) {
                iconName = [iconName stringByAppendingString:@"Medium"];
            } else if (style == 3) {
                iconName = [iconName stringByAppendingString:@"Large"];
            }
            if ([cell isHighlighted]) {
                iconName = [iconName stringByAppendingString:@"Selected"];
            }
            image = [NSImage imageAppNamed:iconName];
        }
        [cell setImage:image];
    }
}

- (EditBoardSheetController *)editBoardSheetController
{
	if(_editBoardSheetController) return _editBoardSheetController;
	
	_editBoardSheetController = [[EditBoardSheetController alloc] initWithDelegate:self];
	return _editBoardSheetController;
}
- (AddBoardSheetController *)addBoardSheetController
{
	if (!_addBoardSheetController) {
		_addBoardSheetController = [[AddBoardSheetController alloc] init];
	}
	return _addBoardSheetController;
}


- (BoardListItem *)targetItem
{
	NSUInteger row = [boardListView clickedRow];
	if(row == -1) {
		row = [boardListView selectedRow];
	}
	BoardListItem *item = [boardListView itemAtRow:row];
	return item;
}

+ (NSString *)localizableStringsTableName
{
	return APP_TVIEW_LOCALIZABLE_FILE;
}
@end

#import "SmartBoardListItemEditor.h"

// menuItem tags
#define kBrowserMenuItemAlwaysEnabledTag	777

#define kBLEditItemViaMenubarItemTag		751
#define kBLEditItemViaContMenuItemTag		701
#define kBLDeleteItemViaMenubarItemTag		752
#define kBLDeleteItemViaContMenuItemTag		702
#define kBLOpenBoardItemViaContMenuItemTag	703
#define kBLShowInspectorViaContMenuItemTag	704
#define kBLShowLocalRulesViaContMenuItemTag 705

#define kBLContMenuItemTagMin		700
#define kBLContMenuItemTagMax		706

#define kTLContMenuLabelMenuTag     789

static NSString *const kRemoveDrawerItemTitleKey    = @"Browser Del Drawer Item Title";
static NSString *const kRemoveDrawerItemMsgKey      = @"Browser Del Board Items Message";

@implementation KMBoardList (ListEditor)
- (void)controller:(EditBoardSheetController *)controller didEndSheetWithReturnCode:(NSInteger)code
{
	// do noting.
}
- (BOOL)reselectBoard:(id)boardListItem
{
	if (!boardListItem) return NO;
	
	NSInteger index = [self.boardListView rowForItem:boardListItem];
	if (index == -1) return NO;
	
	[self.boardListView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
	return YES;
}
- (void)smartBoardDidEdit:(NSNotification *)aNotification
{
	id anotherItem = [aNotification userInfo];
	
	if(self.selection == anotherItem) { // 編集したスマート掲示板を現在表示中である
//       [self showThreadsListForBoard:self.selection forceReload:YES]; // 編集後の条件で再読み込み
		[NSApp sendAction:@selector(reloadThreadsList:) to:nil from:nil];
	}
	[[NSNotificationCenter defaultCenter] removeObserver:self
													name:SBLIEditorDidEditSmartBoardListItemNotification
												  object:nil];
}
- (void)boardItemsDeletionSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(id)contextInfo
{
	UTILAssertKindOfClass(contextInfo, NSIndexSet);
	
	if (returnCode == NSAlertFirstButtonReturn) {
		// 参考：<http://www.cocoadev.com/index.pl?NSIndexSet>
		NSUInteger    arrayElement;
		NSDictionary    *item_;
		NSInteger             size = [contextInfo lastIndex]+1;
		NSRange         e = NSMakeRange(0, size);
		
		NSMutableArray  *boardItemsForRemoving = [NSMutableArray array];
		
		[self.boardListView deselectAll:nil]; // 先に選択を解除しておく
		
		while ([contextInfo getIndexes:&arrayElement maxCount:1 inIndexRange:&e] > 0) {
			item_ = [self.boardListView itemAtRow:arrayElement];
			
			if (item_) [boardItemsForRemoving addObject:item_];
		}
		
		[[BoardManager defaultManager] removeBoardItems:boardItemsForRemoving];
		
		[self reselectBoard:self.selection];
	}
	[contextInfo release];
}

@end

@implementation KMBoardList (Actions)
- (void)setItem:(id)item :(NSInteger *)userInfo
{
	if(item) {
		id userList = [[BoardManager defaultManager] userList];
		
		[userList addItem:item afterObject:nil];
		[self.boardListView reloadData];
	}
}

- (IBAction)addSmartItem:(id)sender
{
	[[SmartBoardListItemEditor editor] cretateFromUIWindow:self.view.window
												  delegate:self
										   settingSelector:@selector(setItem::)
												  userInfo:NULL];
}

- (IBAction)addBoardListItem:(id)sender
{
	[[self addBoardSheetController] beginSheetModalForWindow:self.view.window modalDelegate:self contextInfo:nil];
}

- (IBAction)addCategoryItem:(id)sender
{
	[[self editBoardSheetController] beginAddCategorySheetForWindow:self.view.window];
}
- (IBAction)editBoardListItem:(id)sender
{
	BoardListItem *item = [self targetItem];
	if ([BoardListItem isBoardItem:item]) {
		[self.editBoardSheetController setTargetItem:item];
		[self.editBoardSheetController beginEditBoardSheetForWindow:self.view.window];
	} else if ([BoardListItem isFolderItem:item]) {
		[self.editBoardSheetController setTargetItem:item];
		[self.editBoardSheetController beginEditCategorySheetForWindow:self.view.window];
	} else if ([BoardListItem isSmartItem:item]) {
		SmartBoardListItemEditor *editor = [SmartBoardListItemEditor editor];
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(smartBoardDidEdit:)
													 name:SBLIEditorDidEditSmartBoardListItemNotification
												   object:editor];
		[editor editWithUIWindow:self.view.window smartBoardItem:item];
	}
}
- (IBAction)removeBoardListItem:(id)sender
{
	NSInteger tag_ = [sender tag];
	NSInteger targetRow;
	
	BSBoardListView *boardListTable_ = (BSBoardListView *)self.boardListView;    
	NSIndexSet  *indexSet_;
	
	if (tag_ == kBLDeleteItemViaContMenuItemTag) {
		targetRow = [(BSBoardListView *)boardListTable_ clickedRow];
	} else {
		targetRow = [boardListTable_ selectedRow];
	}
	
	if (targetRow == -1) return;
	
	if ([boardListTable_ numberOfSelectedRows] == 1) {
		indexSet_ = [[NSIndexSet alloc] initWithIndex:targetRow];
	} else {
		NSIndexSet *selectedRows = [boardListTable_ selectedRowIndexes];
		if (tag_ == kBLDeleteItemViaMenubarItemTag) {
			indexSet_ = [[NSIndexSet alloc] initWithIndexSet:selectedRows];
		} else {
			if ([selectedRows containsIndex:targetRow]) {
				indexSet_ = [[NSIndexSet alloc] initWithIndexSet:selectedRows];
			} else { // 複数選択項目とは別の項目を semiSelect した
				indexSet_ = [[NSIndexSet alloc] initWithIndex:targetRow];
			}
		}
	}
	
	NSAlert *alert_ = [[[NSAlert alloc] init] autorelease];
	[alert_ setAlertStyle:NSWarningAlertStyle];
	[alert_ setMessageText:[self localizedString:kRemoveDrawerItemTitleKey]];
	[alert_ setInformativeText:[self localizedString:kRemoveDrawerItemMsgKey]];
	[alert_ addButtonWithTitle:[self localizedString:@"Deletion OK"]];
	[alert_ addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel")];
	
	NSBeep();
	[alert_ beginSheetModalForWindow:self.view.window
					   modalDelegate:self
					  didEndSelector:@selector(boardItemsDeletionSheetDidEnd:returnCode:contextInfo:)
						 contextInfo:indexSet_];
}

- (IBAction)showBoardInspectorPanel:(id)sender
{
	BoardListItem *item = [self targetItem];
	
	if(![item hasURL]) return;
	NSString *boardName = [item name];
	if(!boardName) return;
	
	[[BSBoardInfoInspector sharedInstance] showInspectorForTargetBoard:boardName];
}
- (IBAction)showLocalRules:(id)sender
{
	BoardListItem *item = [self targetItem];
	BoardManager *bm = [BoardManager defaultManager];
	NSString *boardName = [item name];
	id controller = [bm localRulesPanelControllerForBoardName:boardName];
	[controller showWindow:self];
}
- (IBAction)openBBSInBrowser:(id)sender
{
	[[NSWorkspace sharedWorkspace] openURL:[[self targetItem] url] inBackground:[CMRPref openInBg]];
}


- (IBAction)boardListViewDoubleAction:(id)sender
{
	UTILAssertKindOfClass(sender, NSOutlineView);
	
	NSInteger	rowNum = [sender clickedRow];
	if (-1 == rowNum) return;
	
	id item_ = [sender itemAtRow:rowNum];
	
	if ([sender isExpandable:item_]) {
		if ([sender isItemExpanded:item_]) {
			[sender collapseItem:item_];
		} else {
			[sender expandItem:item_];
		}
	}
}
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)theItem
{
	SEL action = [theItem action];
	BoardListItem *targetItem = [self targetItem];
	
	if(action == @selector(showBoardInspectorPanel:)) {
		if(![targetItem hasURL]) return NO;
		if(![targetItem name]) return NO;
		return YES;
	}
	if(action == @selector(editBoardListItem:)
	   || action == @selector(removeBoardListItem:)) {
		return ![BoardListItem isFavoriteItem:targetItem];
	}
	if(action == @selector(showLocalRules:)) {
		return [BoardListItem isBoardItem:targetItem];
	}
	if(action == @selector(openBBSInBrowser:)) {
		return [targetItem hasURL];
	}
	return YES;
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
	return [self validateUserInterfaceItem:menuItem];
}
@end
