//
//  KMBSLogViewDelegate.m
//  BathyScaphe
//
//  Created by 堀 昌樹 on 12/04/22.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#import "KMBSLogViewDelegate.h"
#import "CMRThreadView.h"

#import "KMBSLogPopUp.h"

#import "AppDefaults.h"
#import "CMRThreadAttributes.h"
#import "CMRMessageAttributesTemplate.h"
#import "NSCharacterSet+CMXAdditions.h"

#import <SGAppKit/SGAppKit.h>


@implementation KMBSLogViewDelegate
@synthesize doc = _doc;
@synthesize owner = _owner;

+ (NSString *)localizableStringsTableName
{
#define APP_TVIEW_LOCALIZABLE_FILE			@"ThreadViewer"

	return APP_TVIEW_LOCALIZABLE_FILE;
}

- (NSWindow *)window
{
	return self.owner.window;
}

- (void)colorizeID:(NSTextView *)textView
{
	[self.owner colorizeID:textView];
}

#pragma mark NSTextView Delegate
- (BOOL)textView:(NSTextView *)textView clickedOnLink:(id)aLink atIndex:(NSUInteger)charIndex
{
	return [self.owner.linkHandler clickedOnLink:aLink];
}

#pragma mark CMRThreadView delegate
- (CMRThreadSignature *)threadSignatureForView:(CMRThreadView *)aView
{
	return [self.doc.threadAttr threadSignature];
}

- (CMRThreadLayout *)threadLayoutForView:(CMRThreadView *)aView
{
	return (CMRThreadLayout *)self.owner;
}

- (void)threadView:(CMRThreadView *)aView replyTo:(NSIndexSet *)messageIndexes
{
	[self.owner replyTo:messageIndexes];
}

// Available in Starlight Breaker.
- (void)threadView:(CMRThreadView *)aView reverseAnchorPopUp:(NSUInteger)targetIndex locationHint:(NSPoint)location_
{
	[self.owner.popUp tryShowPopUpReverseAnchor:targetIndex locationHint:location_];
}

/* CMRThreadViewerRunSpamFilterNotification */
#pragma mark -
/* #####          EXTERNAL VARIABLES            ##### */
extern NSString *CMRThreadViewerRunSpamFilterNotification;
/* ######################################################## */

- (void)threadViewerRunSpamFilter:(NSNotification *)theNotification
{
#warning THIS MUST IMPLEMENT
	//	UTILAssertNotificationName(theNotification, CMRThreadViewerRunSpamFilterNotification);
	//	
	//    id object = [theNotification object];
	//    if ((object == self) || (m_addNGExWindowController && (object == m_addNGExWindowController))) {
	//        if ([CMRPref spamFilterEnabled]) {
	//            [self runSpamFilter:nil];
	//        }        
	//    }
}
- (void)postRunSpamFilterNotification
{
	NSNotification *notification;
    NSNotificationQueue *queue = [NSNotificationQueue defaultQueue];
	
	notification = [NSNotification notificationWithName:CMRThreadViewerRunSpamFilterNotification object:self];
	[queue enqueueNotification:notification
                  postingStyle:NSPostWhenIdle
                  coalesceMask:NSNotificationCoalescingOnSender
                      forModes:nil];
}
- (void)threadView:(CMRThreadView *)aView spam:(CMRThreadMessage *)aMessage messageRegister:(BOOL)registerFlag
{
	if(registerFlag) {
		[self.doc registerSpam:aMessage];
		[self postRunSpamFilterNotification];	// 新しいサンプルを追加した場合のみ自動的に起動
	} else {
		[self.doc unregisterSpam:aMessage];
	}
}

- (BOOL)threadView:(CMRThreadView *)aView
	  mouseClicked:(NSEvent *)theEvent
		   atIndex:(NSUInteger)charIndex
	  messageIndex:(NSUInteger)aMessageIndex
{
	if ([theEvent modifierFlags] & NSAlternateKeyMask) {
		NSPoint	winLocation = [theEvent locationInWindow];
		NSPoint	screenLocation = [[aView window] convertBaseToScreen: winLocation]; 
		[self threadView:aView reverseAnchorPopUp:aMessageIndex locationHint:screenLocation];
	} else {
		NSMenu	*menu_ = [aView messageMenuWithMessageIndex:aMessageIndex];
		[NSMenu popUpContextMenu:menu_ withEvent:theEvent forView:aView];
	}
	return YES;
}

#pragma mark Gesuture Support

- (BOOL)threadView:(CMRThreadView *)aView swipeWithEvent:(NSEvent *)theEvent
{
#warning TODO:他の方式で返信可能にする事
	// NOを返してもダメなので
	// 強引に swipWithEvent: をCMRThreadViewの次のレスポンダに投げる。
	[[aView nextResponder] swipeWithEvent:theEvent];
	
    return YES;
}

- (void)threadView:(CMRThreadView *)aView magnifyEnough:(CGFloat)additionalScaleFactor
{
	[self.owner magnifyEnough:additionalScaleFactor];
}

- (void)threadView:(CMRThreadView *)aView rotateEnough:(CGFloat)rotatedDegree
{
	[self.owner rotateEnough:rotatedDegree];
}

- (void)threadView:(CMRThreadView *)aView didFinishRotating:(CGFloat)rotatedDegree
{
    [self.doc reloadThread:self];
}

- (BOOL)acceptsFirstResponderForView:(CMRThreadView *)aView
{
    return YES;
}

#pragma mark SGHTMLView delegate
- (NSArray *)HTMLViewFilteringLinkSchemes:(SGHTMLView *)aView
{
	// "cmonar:", "mailto:", "cmbe:" をフィルタ
	static NSArray *cachedLinkSchemes = nil;
	if (!cachedLinkSchemes) {
		cachedLinkSchemes = [[NSArray alloc] initWithObjects:CMRAttributeInnerLinkScheme, CMRAttributesBeProfileLinkScheme, @"mailto", @"sssp", nil];
	}
	return cachedLinkSchemes;
}

- (void)HTMLView:(SGHTMLView *)aView mouseEnteredInLink:(id)aLink inTrackingRect:(NSRect)aRect withEvent:(NSEvent *)anEvent
{
	NSPoint			location_;
	
	location_ = NSEqualRects(aRect, NSZeroRect) ? [anEvent locationInWindow] : aRect.origin;
	location_ = [aView convertPoint:location_ toView:nil];
	location_ = [[aView window] convertBaseToScreen:location_];
	location_.y -= 1.0f;
	
	[self.owner.popUp tryShowPopUpWindowWithLink:aLink locationHint:location_];
}

- (void)HTMLView:(SGHTMLView *)aView mouseExitedFromLink:(id)aLink inTrackingRect:(NSRect)aRect withEvent:(NSEvent *)anEvent
{
	[self.owner.popUp closePopUpWindowForObject:aLink];
}

// continuous mouseDown
- (BOOL)HTMLView:(SGHTMLView *)aView shouldHandleContinuousMouseDown:(NSEvent *)theEvent
{
	NSRange		selectedRange_;
	id			v;
	unichar		c;
	NSPoint		mouseLocation_;
	
	// ID ポップアップ
	mouseLocation_ = [aView convertPoint:[theEvent locationInWindow] fromView:nil];
	
	v = [aView attribute:BSMessageIDAttributeName atPoint:mouseLocation_ effectiveRange:NULL];
	
	if (v) return YES;
	
	selectedRange_ = [aView selectedRange];
	if (0 == selectedRange_.length) return NO;
	
	// レス番号ではポップアップしない
	v = [[aView textStorage] attribute:CMRMessageIndexAttributeName 
							   atIndex:selectedRange_.location
						effectiveRange:NULL];
	if (v) return NO;
	
	c = [[aView string] characterAtIndex:selectedRange_.location];
	return [[NSCharacterSet numberCharacterSet_JP] characterIsMember:c];
}

- (BOOL)HTMLView:(SGHTMLView *)aView continuousMouseDown:(NSEvent *)theEvent
{
	NSPoint	mouseLoc_;
	BOOL	isInside_;
	
	UTILRequireCondition((aView && theEvent), default_implementation);
	
	if([self.owner.popUp tryShowPopUpIDForEevent:theEvent]) return NO;
	
	NSRange				selectedRange_;
	NSLayoutManager		*layoutManager_;
	NSRange				selectedGlyphRange_;
	NSRect				selection_;
	
	selectedRange_ = [aView selectedRange];
	UTILRequireCondition(selectedRange_.length, default_implementation);
	
	layoutManager_ = [aView layoutManager];
	UTILRequireCondition(layoutManager_, default_implementation);
	
	selectedGlyphRange_ = 
	[layoutManager_ glyphRangeForCharacterRange:selectedRange_
						   actualCharacterRange:NULL];
	UTILRequireCondition(selectedGlyphRange_.length, default_implementation);
	selection_ = 
	[layoutManager_ boundingRectForGlyphRange:selectedGlyphRange_
							  inTextContainer:[aView textContainer]];
	isInside_ = [aView mouse:mouseLoc_ inRect:selection_];
	//		UTILRequireCondition(isInside_, default_implementation);
	// 暫定
	if (!isInside_) {
		return ([CMRPref mouseDownTrackingTime] > 0);
	}
	
	mouseLoc_ = (NSPeriodic == [theEvent type])
		? [[aView window] convertScreenToBase:[theEvent locationInWindow]]
		: [theEvent locationInWindow];
	mouseLoc_ = [aView convertPoint:mouseLoc_ fromView:nil];
	mouseLoc_.y = [aView isFlipped] ? NSMinY(selection_) : NSMaxY(selection_);
	mouseLoc_ = [aView convertPoint:mouseLoc_ toView:nil];
	mouseLoc_ = [[aView window] convertBaseToScreen:mouseLoc_];
	
	// テキストのドラッグを許すように、ここでは常にNOを返す。
	[self.owner.popUp tryShowPopUpWindowSubstringWithRange:selectedRange_
									   inTextStorage:[aView textStorage]
										locationHint:mouseLoc_];
	return NO;
	
default_implementation:
	return YES;
}
@end
