// 8 december 2015
#import "uipriv_darwin.h"

// NSTextView has no intrinsic content size by default, which wreaks havoc on a pure-Auto Layout system
// we'll have to take over to get it to work
// see also http://stackoverflow.com/questions/24210153/nstextview-not-properly-resizing-with-auto-layout and http://stackoverflow.com/questions/11237622/using-autolayout-with-expanding-nstextviews
@interface intrinsicSizeTextView : NSTextView {
	uiMultilineEntry *libui_e;
}
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e;
@end

struct uiMultilineEntry {
	uiDarwinControl c;
	NSScrollView *sv;
	intrinsicSizeTextView *tv;
	uiprivScrollViewData *d;
	void (*onChanged)(uiMultilineEntry *, void *);
	void *onChangedData;
	BOOL changing;
};

@implementation intrinsicSizeTextView

- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e
{
	self = [super initWithFrame:r];
	if (self)
		self->libui_e = e;
	return self;
}

- (NSSize)intrinsicContentSize
{
	NSTextContainer *textContainer;
	NSLayoutManager *layoutManager;
	NSRect rect;

	textContainer = [self textContainer];
	layoutManager = [self layoutManager];
	[layoutManager ensureLayoutForTextContainer:textContainer];
	rect = [layoutManager usedRectForTextContainer:textContainer];
	return rect.size;
}

- (void)didChangeText
{
	[super didChangeText];
	[self invalidateIntrinsicContentSize];
	if (!self->libui_e->changing)
		(*(self->libui_e->onChanged))(self->libui_e, self->libui_e->onChangedData);
}

@end

uiDarwinControlAllDefaultsExceptDestroy(uiMultilineEntry, sv)

static void uiMultilineEntryDestroy(uiControl *c)
{
	uiMultilineEntry *e = uiMultilineEntry(c);

	uiprivScrollViewFreeData(e->sv, e->d);
	[e->tv release];
	[e->sv release];
	uiFreeControl(uiControl(e));
}

static void defaultOnChanged(uiMultilineEntry *e, void *data)
{
	// do nothing
}

char *uiMultilineEntryText(uiMultilineEntry *e)
{
	return uiDarwinNSStringToText([e->tv string]);
}

void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text)
{
	[[e->tv textStorage] replaceCharactersInRange:NSMakeRange(0, [[e->tv string] length])
		withString:uiprivToNSString(text)];
	// must be called explicitly according to the documentation of shouldChangeTextInRange:replacementString:
	e->changing = YES;
	[e->tv didChangeText];
	e->changing = NO;
}

// TODO scroll to end?
void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text)
{
	[[e->tv textStorage] replaceCharactersInRange:NSMakeRange([[e->tv string] length], 0)
		withString:uiprivToNSString(text)];
	e->changing = YES;
	[e->tv didChangeText];
	e->changing = NO;
}

void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data)
{
	e->onChanged = f;
	e->onChangedData = data;
}

int uiMultilineEntryReadOnly(uiMultilineEntry *e)
{
	return [e->tv isEditable] == NO;
}

void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly)
{
	BOOL editable;

	editable = YES;
	if (readonly)
		editable = NO;
	[e->tv setEditable:editable];
}

static uiMultilineEntry *finishMultilineEntry(BOOL hscroll)
{
	uiMultilineEntry *e;
	NSFont *font;
	uiprivScrollViewCreateParams p;

	uiDarwinNewControl(uiMultilineEntry, e);

	e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect e:e];
if (@available(macOS 10.14, *)) {
	[e->tv setUsesAdaptiveColorMappingForDarkAppearance:YES];
}

	// verified against Interface Builder for a sufficiently customized text view

	// NSText properties:
	// this is what Interface Builder sets the background color to
	[e->tv setBackgroundColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]];
	[e->tv setDrawsBackground:YES];
	[e->tv setEditable:YES];
	[e->tv setSelectable:YES];
	[e->tv setFieldEditor:NO];
	[e->tv setRichText:NO];
	[e->tv setImportsGraphics:NO];
	[e->tv setUsesFontPanel:NO];
	[e->tv setRulerVisible:NO];
	// we'll handle font last
	// while setAlignment: has been around since 10.0, the named constant "NSTextAlignmentNatural" seems to have only been introduced in 10.11
#define ourNSTextAlignmentNatural 4
	[e->tv setAlignment:ourNSTextAlignmentNatural];
	// textColor is set to nil, just keep the dfault
	[e->tv setBaseWritingDirection:NSWritingDirectionNatural];
	[e->tv setHorizontallyResizable:NO];
	[e->tv setVerticallyResizable:YES];

	// NSTextView properties:
	[e->tv setAllowsDocumentBackgroundColorChange:NO];
	[e->tv setAllowsUndo:YES];
	// default paragraph style is nil; keep default
	[e->tv setAllowsImageEditing:NO];
	[e->tv setAutomaticQuoteSubstitutionEnabled:NO];
	[e->tv setAutomaticLinkDetectionEnabled:NO];
	[e->tv setDisplaysLinkToolTips:YES];
	[e->tv setUsesRuler:NO];
	[e->tv setUsesInspectorBar:NO];
	[e->tv setSelectionGranularity:NSSelectByCharacter];
	// there is a dedicated named insertion point color but oh well
	[e->tv setInsertionPointColor:[NSColor controlTextColor]];
	// typing attributes is nil; keep default (we change it below for fonts though)
	[e->tv setSmartInsertDeleteEnabled:NO];
	[e->tv setContinuousSpellCheckingEnabled:NO];
	[e->tv setGrammarCheckingEnabled:NO];
	[e->tv setUsesFindPanel:YES];
	[e->tv setEnabledTextCheckingTypes:0];
	[e->tv setAutomaticDashSubstitutionEnabled:NO];
	[e->tv setAutomaticDataDetectionEnabled:NO];
	[e->tv setAutomaticSpellingCorrectionEnabled:NO];
	[e->tv setAutomaticTextReplacementEnabled:NO];
	[e->tv setUsesFindBar:NO];
	[e->tv setIncrementalSearchingEnabled:NO];

	// NSTextContainer properties:
	[[e->tv textContainer] setWidthTracksTextView:YES];
	[[e->tv textContainer] setHeightTracksTextView:NO];

	// NSLayoutManager properties:
	[[e->tv layoutManager] setAllowsNonContiguousLayout:YES];

	// now just to be safe; this will do some of the above but whatever
	uiprivDisableAutocorrect(e->tv);

	// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html
	// notice we don't use the Auto Layout code; see scrollview.m for more details
	[e->tv setMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
	[e->tv setVerticallyResizable:YES];
	[e->tv setHorizontallyResizable:hscroll];
	if (hscroll) {
		[e->tv setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
		[[e->tv textContainer] setWidthTracksTextView:NO];
	} else {
		[e->tv setAutoresizingMask:NSViewWidthSizable];
		[[e->tv textContainer] setWidthTracksTextView:YES];
	}
	[[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];

	// don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font
	font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
	[e->tv setTypingAttributes:[NSDictionary
		dictionaryWithObject:font
		forKey:NSFontAttributeName]];
	// e->tv font from Interface Builder is nil, but setFont:nil throws an exception
	// let's just set it to the standard control font anyway, just to be safe
	[e->tv setFont:font];

	memset(&p, 0, sizeof (uiprivScrollViewCreateParams));
	p.DocumentView = e->tv;
	// this is what Interface Builder sets it to
	p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
	p.DrawsBackground = YES;
	p.Bordered = YES;
	p.HScroll = hscroll;
	p.VScroll = YES;
	e->sv = uiprivMkScrollView(&p, &(e->d));

	uiMultilineEntryOnChanged(e, defaultOnChanged, NULL);

	return e;
}

uiMultilineEntry *uiNewMultilineEntry(void)
{
	return finishMultilineEntry(NO);
}

uiMultilineEntry *uiNewNonWrappingMultilineEntry(void)
{
	return finishMultilineEntry(YES);
}
