//
//  XTTextRecolorationHelper.m
//  XTads
//
//  Created by Rune Berg on 05/05/2020.
//  Copyright © 2020 Rune Berg. All rights reserved.
//

#import "XTTextRecolorationHelper.h"
#import "XTOutputFormatterProtocol.h"
#import "XTPrefs.h"


@interface XTTextRecolorationHelper ()

@property XTPrefs *prefs;
@property (weak) NSTextStorage *textStorage;
@property BOOL isForBanner;

@end


@implementation XTTextRecolorationHelper

+ (XTTextRecolorationHelper *)forTextStorage:(NSTextStorage *)textStorage
								 isForBanner:(BOOL)isForBanner
{
	XTTextRecolorationHelper *helper = [[XTTextRecolorationHelper alloc] initWithTextStorage:textStorage];
	helper.isForBanner = isForBanner;
	return helper;
}

- (instancetype)initWithTextStorage:(NSTextStorage *)textStorage
{
	self = [super init];
	if (self) {
		_prefs = [XTPrefs prefs];
		_textStorage = textStorage;
		_isForBanner = NO;
	}
	return self;
}

- (void)applyTextHtmlColor:(NSRange)range
			   colorResult:(XTOutputTextColorResult *)colorResult
			   colorSource:(XTColorSource)colorSource
			 textHtmlColor:(XTHtmlColor *)textHtmlColor
			inputHtmlColor:(XTHtmlColor *)inputHtmlColor
				forBodyTag:(BOOL)forBodyTag
				forceApply:(BOOL)forceApply
	  allowGameToSetColors:(BOOL)allowGameToSetColors
{
	//NSString *s = [textStorage attributedSubstringFromRange:range].string; // keep for debugging
	
	NSTextStorage *textStorage = self.textStorage;

	if (colorSource == XT_COLOR_SOURCE_OUTPUT_FROM_BODY) {
		BOOL shouldRecolor = NO;
		if (forBodyTag) {
			shouldRecolor = YES;
		} else if (colorResult.htmlColor.isForParameterized) {
			shouldRecolor = YES;
			textHtmlColor = colorResult.htmlColor;
		} else if (! allowGameToSetColors) {
			shouldRecolor = YES;
		}
		if (shouldRecolor) {
			NSColor *textColor = nil;
			if (textHtmlColor != nil) {
				if (allowGameToSetColors) {
					textColor = textHtmlColor.color;
				}
			}
			if ((textColor == nil) && allowGameToSetColors) {
				// illegal color
				textColor = [self getPrefsOutputTextColor];
				XTHtmlColor *newHtmlColor = [XTHtmlColor forNSColor:textColor];
				XTOutputTextColorResult *newColorResult = [XTOutputTextColorResult forHtmlColor:newHtmlColor colorSource:XT_COLOR_SOURCE_OUTPUT_FROM_PREFS];
				[textStorage addAttribute:XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT value:newColorResult range:range];
			} else if (forBodyTag) {
				XTOutputTextColorResult *newColorResult = [XTOutputTextColorResult forHtmlColor:textHtmlColor colorSource:XT_COLOR_SOURCE_OUTPUT_FROM_BODY];
				[textStorage addAttribute:XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT value:newColorResult range:range];
			} else {
				//TODO !!! adapt: need to set XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT here?
				int brkpt = 1;
			}
			if (textColor == nil) {
				textColor = [self getPrefsOutputTextColor];
			}
			[textStorage addAttribute:NSForegroundColorAttributeName value:textColor range:range];
		}
	} else if (colorSource == XT_COLOR_SOURCE_OUTPUT_FROM_PREFS) {
		if (textHtmlColor != nil) {
			NSColor *textColor = nil;
			if (allowGameToSetColors) {
				if (colorResult.bannerTextColorFromMainAreaPrefs && ! forBodyTag) {
					textColor = self.prefs.outputAreaTextColor.value;
				} else {
					textColor = textHtmlColor.color;
				}
			}
			if (textColor == nil) {
				textColor = [self getPrefsOutputTextColor];
			}
			[textStorage addAttribute:NSForegroundColorAttributeName value:textColor range:range];
			if (forBodyTag) {
				XTOutputTextColorResult *newColorResult = [XTOutputTextColorResult forHtmlColor:textHtmlColor colorSource:XT_COLOR_SOURCE_OUTPUT_FROM_BODY]; //...
				[textStorage addAttribute:XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT value:newColorResult range:range];
			}
		}
	} else if (colorSource == XT_COLOR_SOURCE_INPUT_FROM_BODY) {
		BOOL shouldRecolor = NO;
		if (forBodyTag) {
			shouldRecolor = YES;
		} else if (colorResult.htmlColor.isForParameterized) {
			shouldRecolor = YES;
			inputHtmlColor = colorResult.htmlColor;
		} else if (! allowGameToSetColors) {
			shouldRecolor = YES;
		}
		if (shouldRecolor) {
			NSColor *inputColor = nil;
			if (inputHtmlColor != nil) {
				if (allowGameToSetColors) {
					inputColor = inputHtmlColor.color;
				}
			}
			if ((inputColor == nil) && allowGameToSetColors) {
				// illegal color
				inputColor = self.prefs.inputTextColor.value;
				if (inputHtmlColor.color == nil) {
					XTHtmlColor *newHtmlColor = [XTHtmlColor forNSColor:inputColor];
					XTOutputTextColorResult *newColorResult = [XTOutputTextColorResult forHtmlColor:newHtmlColor  colorSource:XT_COLOR_SOURCE_INPUT_FROM_PREFS];
					[textStorage addAttribute:XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT value:newColorResult range:range];
				}
			} else if (forBodyTag) {
				XTOutputTextColorResult *newColorResult = [XTOutputTextColorResult forHtmlColor:inputHtmlColor colorSource:XT_COLOR_SOURCE_INPUT_FROM_BODY];
				[textStorage addAttribute:XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT value:newColorResult range:range];
			} else {
				//TODO !!! adapt: need to set XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT here?
				int brkpt = 1;
			}
			if (inputColor == nil) {
				inputColor = self.prefs.inputTextColor.value;
			}
			[textStorage addAttribute:NSForegroundColorAttributeName value:inputColor range:range];
		}
	} else if (colorSource == XT_COLOR_SOURCE_INPUT_FROM_PREFS) {
		if (inputHtmlColor != nil) {
			NSColor *inputColor = nil;
			if (allowGameToSetColors) {
				inputColor = inputHtmlColor.color;
			}
			XTColorSource newColorSource = XT_COLOR_SOURCE_INPUT_FROM_BODY;
			if (inputColor == nil) {
				inputColor = self.prefs.inputTextColor.value;
				if (inputHtmlColor.color == nil) {
					// invalid inputHtmlColor
					newColorSource = XT_COLOR_SOURCE_INPUT_FROM_PREFS;
				}
			}
			[textStorage addAttribute:NSForegroundColorAttributeName value:inputColor range:range];
			if (forBodyTag) {
				XTOutputTextColorResult *newColorResult = [XTOutputTextColorResult forHtmlColor:inputHtmlColor colorSource:newColorSource];
				[textStorage addAttribute:XT_OUTPUT_FORMATTER_ATTR_RECOLORABLE_TEXT value:newColorResult range:range];
			}
		}
	} else if (colorSource == XT_COLOR_SOURCE_OUTPUT_FROM_FONT) {
		if ((! forBodyTag) || forceApply) {
			// prefs changed
			NSColor *textColor = nil;
			if (allowGameToSetColors) {
				textColor = colorResult.htmlColor.color;
			}
			if (textColor != nil) {
				[textStorage addAttribute:NSForegroundColorAttributeName value:textColor range:range];
			} else {
				if (forceApply) {
					[textStorage removeAttribute:NSForegroundColorAttributeName range:range];
				}
			}
		}
	} else if (colorResult != nil) {
		int brkpt = 1;
	}
}

- (void)removeTextColorsSetByGameForRange:(NSRange)range
							  colorResult:(XTOutputTextColorResult *)colorResult
							  colorSource:(XTColorSource)colorSource
{
	NSColor *textColor = nil;
	
	switch (colorSource) {
		case XT_COLOR_SOURCE_OUTPUT_FROM_BODY:
		case XT_COLOR_SOURCE_OUTPUT_FROM_PREFS:
		case XT_COLOR_SOURCE_OUTPUT_FROM_FONT:
			textColor = [self getPrefsOutputTextColor];
			break;
		case XT_COLOR_SOURCE_INPUT_FROM_BODY:
		case XT_COLOR_SOURCE_INPUT_FROM_PREFS:
			textColor = self.prefs.inputTextColor.value;
			break;
		default:
			break;
	}

	[self.textStorage addAttribute:NSForegroundColorAttributeName value:textColor range:range];
}

- (void)restoreTextColorsSetByGameForRange:(NSRange)range
							   colorResult:(XTOutputTextColorResult *)colorResult
							   colorSource:(XTColorSource)colorSource
{
	NSColor *textColor = nil;
	
	switch (colorSource) {
		case XT_COLOR_SOURCE_OUTPUT_FROM_BODY:
		case XT_COLOR_SOURCE_OUTPUT_FROM_FONT: {
			XTHtmlColor *textHtmlColor = colorResult.htmlColor;
			textColor = textHtmlColor.color;
			if (textColor == nil) {
				textColor = [self getPrefsOutputTextColor];
			}
			break;
		}
		case XT_COLOR_SOURCE_OUTPUT_FROM_PREFS:
			//TODO !!! adapt: ...same rules as in formatter
			if (colorResult.bannerTextColorFromMainAreaPrefs) {
				textColor = self.prefs.outputAreaTextColor.value;
			} else {
				textColor = [self getPrefsOutputTextColor];
			}
			break;
		case XT_COLOR_SOURCE_INPUT_FROM_BODY: {
			XTHtmlColor *textHtmlColor = colorResult.htmlColor;
			textColor = textHtmlColor.color;
			if (textColor == nil) {
				textColor = self.prefs.inputTextColor.value;
			}
			break;
		}
		case XT_COLOR_SOURCE_INPUT_FROM_PREFS:
			textColor = self.prefs.inputTextColor.value;
			break;
		default:
			break;
	}

	[self.textStorage addAttribute:NSForegroundColorAttributeName value:textColor range:range];
}

- (void)applyTextHtmlBackgroundColor:(NSRange)range
			   colorResult:(XTOutputTextColorResult *)colorResult
			   colorSource:(XTColorSource)colorSource
				forceApply:(BOOL)forceApply
	  allowGameToSetColors:(BOOL)allowGameToSetColors
{
	//NSString *s = [textStorage attributedSubstringFromRange:range].string; // keep for debugging

	NSTextStorage *textStorage = self.textStorage;
	
	if (colorSource == XT_COLOR_SOURCE_BACKGROUND_FROM_FONT) {
		NSColor *bgColor = nil;
		if (allowGameToSetColors) {
			bgColor = colorResult.htmlColor.color;
		}
		if (bgColor != nil) {
			[textStorage addAttribute:NSBackgroundColorAttributeName value:bgColor range:range];
		} else {
			if (forceApply) {
				[textStorage removeAttribute:NSBackgroundColorAttributeName range:range];
			}
		}
	} else if (colorResult != nil) {
		int brkpt = 1;
	}
}

- (void)removeTextBackgroundColorsSetByGameForRange:(NSRange)range
										colorResult:(XTOutputTextColorResult *)colorResult
										colorSource:(XTColorSource)colorSource
{
	if (colorSource == XT_COLOR_SOURCE_BACKGROUND_FROM_FONT) {
		[self.textStorage removeAttribute:NSBackgroundColorAttributeName range:range];
	}
}

- (void)restoreTextBackgroundColorsSetByGameForRange:(NSRange)range
										 colorResult:(XTOutputTextColorResult *)colorResult
										 colorSource:(XTColorSource)colorSource
{
	if (colorSource == XT_COLOR_SOURCE_BACKGROUND_FROM_FONT) {
		NSColor *bgColor = colorResult.htmlColor.color;
		if (bgColor != nil) {
			[self.textStorage addAttribute:NSBackgroundColorAttributeName value:bgColor range:range];
		}
	}
}

- (NSColor *)getPrefsOutputTextColor
{
	NSColor *res;
	if (self.isForBanner) {
		res = self.prefs.statusLineTextColor.value;
	} else {
		res = self.prefs.outputAreaTextColor.value;
	}
	return res;
}

- (BOOL)allowGameToSetColors
{
	NSNumber *valueObj = self.prefs.allowGamesToSetColors.value;
	BOOL res = (valueObj == nil || valueObj.boolValue);
	return res;
}

@end
