//
//  XTFontManager.m
//  TadsTerp
//
//  Created by Rune Berg on 18/08/14.
//  Copyright (c) 2014 Rune Berg. All rights reserved.
//

#import "XTFontManager.h"
#import "XTParameterizedFont.h"
#import "XTStringUtils.h"
#import "XTPrefs.h"
#import "XTLogger.h"
#import "XTAllocDeallocCounter.h"


@interface XTFontManager ()

@end


@implementation XTFontManager

static XTLogger *logger;
static XTFontManager *singletonInstance = nil;

static CGFloat scaleFactorByHtmlSize[] = {
	0.65,   // size=1
	0.80,
	1.00,   // size=3 (default)
	1.30,
	1.70,
	2.25,
	3.25    // size=7
};

+ (void)initialize
{
	logger = [XTLogger loggerForClass:[XTFontManager class]];
	if (singletonInstance == nil) {
		singletonInstance = [XTFontManager new];
	}
}

+ (id)fontManager
{
	return singletonInstance;
}

OVERRIDE_ALLOC_FOR_COUNTER
OVERRIDE_DEALLOC_FOR_COUNTER

- (id)init
{
    self = [super init];
    if (self) {
		[self initParameterizedFontByName];
    }
    return self;
}

- (void)initParameterizedFontByName
{
	_xtadsDefaultParameterizedFontName = @"xtads-default";
	_xtadsFixedWidthParameterizedFontName = @"xtads-fixedwidth";
	_tadsSerifParameterizedFontName = @"tads-serif";
	_tadsSansParameterizedFontName = @"tads-sans";
	_tadsScriptParameterizedFontName = @"tads-script";
	_tadsTypewriterParameterizedFontName = @"tads-typewriter";
	_tadsInputParameterizedFontName = @"tads-input";
}

- (NSFont *)getFontWithParameterizedName:(NSString *)parameterizedName
{
	NSFont *font = nil;
	
	XTParameterizedFont *paramdFont = [self getParameterizedFontWithName:parameterizedName];
	
	if (paramdFont != nil) {
		CGFloat usedFontSize = [self getFontSizeWithinPrefsMinMax:paramdFont.size];
		font = [NSFont fontWithName:paramdFont.name size:usedFontSize];
	}
	
	return font;
}

- (NSFont *)getFontWithName:(NSArray *)fontNames
				  pointSize:(NSNumber *)pointSize // TODO enlargementFactor ? float
				   htmlSize:(NSNumber *)htmlSize  // 1..7, 3 being default
					   bold:(BOOL)bold
					italics:(BOOL)italics
{
	XT_DEF_SELNAME;
	
	if (htmlSize == nil) {
		htmlSize = [NSNumber numberWithUnsignedInteger:3];
	}
	
	NSMutableArray *fontNamesPlusFallbacks = [NSMutableArray arrayWithArray:fontNames];
	[fontNamesPlusFallbacks addObject:self.xtadsDefaultParameterizedFontName];
	[fontNamesPlusFallbacks addObject:@"Helvetica"];
	
	NSFont *font;
	
	for (NSString *fn in fontNamesPlusFallbacks) {
		
		NSString *name = [XTStringUtils trimLeadingAndTrailingWhitespace:fn];
		XTParameterizedFont *parameterizedFont = [self getParameterizedFontWithName:name];
		NSString *usedName;
		CGFloat usedSize;
		BOOL usedRequestedParameterizedFont = NO;

		if (parameterizedFont != nil) {
			usedName =  parameterizedFont.name;
			usedSize = parameterizedFont.size;
			if ((! [name isEqualToString:self.xtadsDefaultParameterizedFontName]) &&
				(! [name isEqualToString:self.xtadsFixedWidthParameterizedFontName])) {
				usedRequestedParameterizedFont = YES;
			}
		} else {
			usedName = name;
			parameterizedFont = [self getParameterizedFontWithName:self.xtadsDefaultParameterizedFontName];
			usedSize = parameterizedFont.size;
		}
		
		//TODO !!! we always override usedSize here... :
		if (pointSize != nil) {
			usedSize = [pointSize floatValue];
		} else {
			if (htmlSize != nil) {
				NSUInteger htmlSizeUI = [htmlSize unsignedIntegerValue];
				if (htmlSizeUI < 1) {
					htmlSizeUI = 1;
				} else if (htmlSizeUI > 7) {
					htmlSizeUI = 7;
				}
				CGFloat scaleFactor = scaleFactorByHtmlSize[htmlSizeUI - 1];
				usedSize = usedSize * scaleFactor;
			} else {
				XT_WARN_0(@"htmlSize is nil");
			}
		}
		
		usedSize = [self getFontSizeWithinPrefsMinMax:usedSize];
		
		BOOL usedBold = bold;
		BOOL usedItalics = italics;
		/*TODO exp rm for indarkness "help" bug
		if (usedRequestedParameterizedFont) {
			usedBold = NO;
			usedItalics = NO;
		}*/
		
		font = [self tryToGetFontWithName:usedName size:usedSize bold:usedBold italics:usedItalics];
		if (font != nil) {
			XT_TRACE_3(@"return with %@ -> %@ %f", name, usedName, usedSize);
			break;
		} else {
			NSString *msgSuffix = [self getLogMsgForFontWithName:usedName pointSize:pointSize htmlSize:htmlSize bold:bold italics:bold];
			XT_WARN_1(@"failed for %@", msgSuffix);
		}
	}

	if (font == nil) {
		XT_ERROR_0(@"GIVING UP and using system font");
		
		font = [NSFont systemFontOfSize:14.0f];
	}

	return font;
}

//TODO mv to prefsWinCtrl?
- (void)setFont:(NSFont *)font forParameterizedName:(NSString *)parameterizedName
{
	XT_DEF_SELNAME;
	XT_TRACE_2(@"\"%@\" - \"%@\"", font.description, parameterizedName);
	
	NSString *name = [font fontName];
		//TODO ren
	CGFloat pointSize = [font pointSize];
	
	XTParameterizedFont *parameterizedFont = [XTParameterizedFont fontWithName:name size:pointSize];

	XTPrefs *prefs = [XTPrefs prefs];
	
	if ([parameterizedName isEqualToString:self.xtadsDefaultParameterizedFontName]) {
		[prefs updateDefaultFontWithName:name size:pointSize];
		
	} else if ([parameterizedName isEqualToString:self.xtadsFixedWidthParameterizedFontName]) {
		[prefs updateFixedWidthFontWithName:name size:pointSize];
		
	} else if ([parameterizedName isEqualToString:self.tadsSerifParameterizedFontName]) {
		[prefs updateSerifedFontWithName:name size:pointSize];
		
	} else if ([parameterizedName isEqualToString:self.tadsSansParameterizedFontName]) {
		[prefs updateSansSerifFontWithName:name size:pointSize];
		
	} else if ([parameterizedName isEqualToString:self.tadsScriptParameterizedFontName]) {
		[prefs updateScriptFontWithName:name size:pointSize];
		
	} else if ([parameterizedName isEqualToString:self.tadsTypewriterParameterizedFontName]) {
		[prefs updateTypewriterFontWithName:name size:pointSize];
		
	} else if ([parameterizedName isEqualToString:self.tadsInputParameterizedFontName]) {
		[prefs updateInputFontWithName:name size:pointSize];
		
	} else {
		XT_ERROR_1(@"got unknown parameterizedName \"%@\"", parameterizedName);
	}
	
	XT_TRACE_2(@"\"%@\" -> \"%@\"", parameterizedName, parameterizedFont);
}

//------  internal methods ---------

- (NSString *)getLogMsgForFontWithName:(NSString *)familyName
				  pointSize:(NSNumber *)pointSize
				   htmlSize:(NSNumber *)htmlSize
					   bold:(BOOL)bold
					italics:(BOOL)italics
{
	NSString *res = [NSString stringWithFormat:@"familyName=\"%@\" pointSize=%f htmlSize=%f bold=%d italics=%d",
					 familyName, pointSize.floatValue, htmlSize.floatValue, bold, italics];
	return res;
}

- (CGFloat)getFontSizeWithinPrefsMinMax:(CGFloat)requestedFontSize
{
	XTPrefs *prefs = [XTPrefs prefs];
	NSInteger minFontSize = prefs.minAllowedFontSize.value.integerValue;
	NSInteger res = requestedFontSize;
	if (res < minFontSize) {
		res = minFontSize;
	} else {
		NSInteger maxFontSize = prefs.maxAllowedFontSize.value.integerValue;
		if (res > maxFontSize) {
			res = maxFontSize;
		}
	}
	return (CGFloat)res;
}

- (NSFont *)tryToGetFontWithName:(NSString *)familyName
							size:(CGFloat)size
							bold:(BOOL)bold
						 italics:(BOOL)italics
{
	NSFont *font = [self doTryToGetFontWithName:familyName size:size bold:bold italics:italics];
	
	// Not all fonts support bold and/or italics, so try without bold and/or italics...
	if (font == nil && bold) {
		font = [self doTryToGetFontWithName:familyName size:size bold:NO italics:italics];
	}
	if (font == nil && italics) {
		font = [self doTryToGetFontWithName:familyName size:size bold:bold italics:NO];
	}
	if (font == nil) {
		font = [self doTryToGetFontWithName:familyName size:size bold:NO italics:NO];
	}
	return font;
}

- (NSFont *)doTryToGetFontWithName:(NSString *)familyName
							size:(CGFloat)size
							bold:(BOOL)bold
						 italics:(BOOL)italics
{
	XT_DEF_SELNAME;
	
	NSString *boldBit = (bold ? @" Bold" : @"");
	NSString *italicsBit = (italics ? @" Oblique" : @"");
	NSString *nsFontName = [NSString stringWithFormat:@"%@%@%@", familyName, boldBit, italicsBit];
	NSFont *font = [NSFont fontWithName:nsFontName size:size];

	// Some fonts us "Italic" instead of "Oblique":
	if (font == nil && italics) {
		italicsBit = @" Italic";
		nsFontName = [NSString stringWithFormat:@"%@%@%@", familyName, boldBit, italicsBit];
		font = [NSFont fontWithName:nsFontName size:size];
	}
	
	if (font == 0) {
		XT_TRACE_4(@"%@: size:%f bold:%d italics:%d - failed to get font", familyName, size, bold, italics);
	}

	return font;
}

- (XTParameterizedFont *)getParameterizedFontWithName:(NSString *)parameterizedName
{
	parameterizedName = [parameterizedName lowercaseString];
	
	NSString *name;
	CGFloat size;
	
	XTPrefs *prefs = [XTPrefs prefs];
	
	if ([parameterizedName isEqualToString:self.xtadsDefaultParameterizedFontName]) {
		name = prefs.defaultFontName.value;
		size = prefs.defaultFontSize.value.floatValue;
		
	} else if ([parameterizedName isEqualToString:self.xtadsFixedWidthParameterizedFontName]) {
		name = prefs.fixedWidthFontName.value;
		size = prefs.fixedWidthFontSize.value.floatValue;
	
	} else if ([parameterizedName isEqualToString:self.tadsSerifParameterizedFontName]) {
		name = prefs.serifedFontName.value;
		size = prefs.serifedFontSize.value.floatValue;
		
	} else if ([parameterizedName isEqualToString:self.tadsSansParameterizedFontName]) {
		name = prefs.sansSerifFontName.value;
		size = prefs.sansSerifFontSize.value.floatValue;
		
	} else if ([parameterizedName isEqualToString:self.tadsScriptParameterizedFontName]) {
		name = prefs.scriptFontName.value;
		size = prefs.scriptFontSize.value.floatValue;
		
	} else if ([parameterizedName isEqualToString:self.tadsTypewriterParameterizedFontName]) {
		name = prefs.typewriterFontName.value;
		size = prefs.typewriterFontSize.value.floatValue;
		
	} else if ([parameterizedName isEqualToString:self.tadsInputParameterizedFontName]) {
		if (prefs.inputFontIsSameAsDefaultFont.value.boolValue) {
			name = prefs.defaultFontName.value;
			size = prefs.defaultFontSize.value.floatValue;
		} else {
			name = prefs.inputFontName.value;
			size = prefs.inputFontSize.value.floatValue;
		}
	} else {
		//XT_ERROR_1(@"got unknown parameterizedName \"%@\"", parameterizedName);
		name = nil;
		//pointSize = 24.0;
	}

	XTParameterizedFont *res = nil;
	if (name != nil) {
		res = [XTParameterizedFont fontWithName:name size:size];
	}
	
	return res;
}

@end
