//
//  XTHtmlColour.m
//  XTads
//
//  Created by Rune Berg on 13/03/2020.
//  Copyright © 2020 Rune Berg. All rights reserved.
//

#import "XTHtmlColor.h"
#import "XTConverter.h"
#import "XTPrefs.h"
#import "XTColorUtils.h"
#import "XTCharacterSets.h"


@interface XTHtmlColor ()

@property NSColor *resolvedColor;
@property NSString *attrValue;

@end


@implementation XTHtmlColor

static NSDictionary *standardHtmlColorByName;
static NSDictionary *paramdColorNameByOsifcValue;

@synthesize attrValue = _attrValue;
@synthesize isForParameterized = _isForParameterized;
@synthesize isTransparent = _isTransparent;

static NSString *PARAMD_COLOR_NAME_TEXT = @"text";
static NSString *PARAMD_COLOR_NAME_BG = @"bgcolor";
static NSString *PARAMD_COLOR_NAME_INPUT = @"input";
static NSString *PARAMD_COLOR_NAME_STATUS_TEXT = @"statustext";
static NSString *PARAMD_COLOR_NAME_STATUS_BG = @"statusbg";
static NSString *PARAMD_COLOR_NAME_A_LINK = @"alink";
static NSString *PARAMD_COLOR_NAME_H_LINK = @"hlink";
static NSString *PARAMD_COLOR_NAME_LINK = @"link";
static NSString *PARAMD_COLOR_NAME_TRANSPARENT = @"transparent";

+ (void)initialize
{
	if (self == [XTHtmlColor class]) {
		[XTHtmlColor initializeStandardHtmlColors];
		[XTHtmlColor initializeParamdColorNameByOsifcValue];
	}
}

+ (void)initializeStandardHtmlColors
{
	// https://en.wikipedia.org/wiki/Web_colors
	
	NSMutableDictionary *mutStandardHtmlColorByName = [NSMutableDictionary dictionaryWithCapacity:20];
	
	CGFloat alpha = 1.0;
	
	mutStandardHtmlColorByName[@"white"]   = [NSColor colorWithSRGBRed:1.0 green:1.0 blue:1.0 alpha:alpha];
	mutStandardHtmlColorByName[@"silver"]  = [NSColor colorWithSRGBRed:0.75 green:0.75 blue:0.75 alpha:alpha];
	mutStandardHtmlColorByName[@"gray"]    = [NSColor colorWithSRGBRed:0.5 green:0.5 blue:0.5 alpha:alpha];
	mutStandardHtmlColorByName[@"black"]   = [NSColor colorWithSRGBRed:0.0 green:0.0 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"red"]     = [NSColor colorWithSRGBRed:1.0 green:0.0 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"maroon"]  = [NSColor colorWithSRGBRed:0.5 green:0.0 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"yellow"]  = [NSColor colorWithSRGBRed:1.0 green:1.0 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"olive"]   = [NSColor colorWithSRGBRed:0.5 green:0.5 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"lime"]    = [NSColor colorWithSRGBRed:0.0 green:1.0 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"green"]   = [NSColor colorWithSRGBRed:0.0 green:0.5 blue:0.0 alpha:alpha];
	mutStandardHtmlColorByName[@"aqua"]    = [NSColor colorWithSRGBRed:0.0 green:1.0 blue:1.0 alpha:alpha];
	mutStandardHtmlColorByName[@"teal"]    = [NSColor colorWithSRGBRed:0.0 green:0.5 blue:0.5 alpha:alpha];
	mutStandardHtmlColorByName[@"blue"]    = [NSColor colorWithSRGBRed:0.0 green:0.0 blue:1.0 alpha:alpha];
	mutStandardHtmlColorByName[@"navy"]    = [NSColor colorWithSRGBRed:0.0 green:0.0 blue:0.5 alpha:alpha];
	mutStandardHtmlColorByName[@"fuchsia"] = [NSColor colorWithSRGBRed:1.0 green:0.0 blue:1.0 alpha:alpha];
	mutStandardHtmlColorByName[@"purple"]  = [NSColor colorWithSRGBRed:0.5 green:0.0 blue:0.5 alpha:alpha];
	
	standardHtmlColorByName = [NSDictionary dictionaryWithDictionary:mutStandardHtmlColorByName];
}

+ (void)initializeParamdColorNameByOsifcValue
{
	NSMutableDictionary *mutDict = [NSMutableDictionary dictionaryWithCapacity:10];
	
	mutDict[[NSNumber numberWithUnsignedInteger:OS_COLOR_P_TRANSPARENT]] = PARAMD_COLOR_NAME_TRANSPARENT;
	mutDict[[NSNumber numberWithUnsignedInteger:OS_COLOR_P_TEXT]] = PARAMD_COLOR_NAME_TEXT;
	mutDict[[NSNumber numberWithUnsignedInteger:OS_COLOR_P_TEXTBG]] = PARAMD_COLOR_NAME_BG;
	mutDict[[NSNumber numberWithUnsignedInteger:OS_COLOR_P_STATUSLINE]] = PARAMD_COLOR_NAME_STATUS_TEXT;
	mutDict[[NSNumber numberWithUnsignedInteger:OS_COLOR_P_STATUSBG]] = PARAMD_COLOR_NAME_STATUS_BG;
	mutDict[[NSNumber numberWithUnsignedInteger:OS_COLOR_P_INPUT]] = PARAMD_COLOR_NAME_INPUT;
	
	paramdColorNameByOsifcValue = [NSDictionary dictionaryWithDictionary:mutDict];
}

- (instancetype)init
{
	self = [super init];
	if (self) {
		_attrValue = nil;
		_resolvedColor = nil;
		_isForParameterized = NO;
		_isTransparent = NO;
	}
	return self;
}

- (instancetype)initWithAttrValue:(NSString *)attrValue
{
	self = [self init];
	if (self) {
		_attrValue = attrValue;
	}
	return self;
}

- (BOOL)isEqualTo:(id)object
{
	/*TODO !!! rm or refine wrt param'd
	if (object == self) {
		return YES;
	}*/
	if (object == nil) {
		return NO;
	}
	if (! [object isKindOfClass:[XTHtmlColor class]]) {
		return NO;
	}
	
	XTHtmlColor *castObject = (XTHtmlColor *)object;
	
	NSColor *thisColor = self.color;
	NSColor *otherColor = castObject.color;
	
	if (thisColor == nil || otherColor == nil) {
		return NO;
	}
	
	if (self.isForParameterized || castObject.isForParameterized) {
		return NO;
	}
	
	if (self.isTransparent != castObject.isTransparent) {
		return NO;
	}
	
	if (! [XTColorUtils color:thisColor isEqualTo:otherColor]) {
		return NO;
	}
	
	return YES;
}

+ (BOOL)safeCompare:(XTHtmlColor *)colorA to:(XTHtmlColor *)colorB
{
	NSUInteger nonNullCount = 0;
	if (colorA != nil) {
		nonNullCount += 1;
	}
	if (colorB != nil) {
		nonNullCount += 1;
	}
	
	BOOL res;
	switch (nonNullCount) {
		case 0:
			res = YES;
			break;
		case 1:
			res = NO;
			break;
		case 2:
			res = [colorA isEqualTo:colorB];
			break;
		default:
			res = NO; // just in case
			break;
	}
	
	return res;
}

+ (XTHtmlColor *)forAttributeValue:(NSString *)attrValue
{
	XTHtmlColor *res = [[XTHtmlColor alloc] initWithAttrValue:attrValue];
	return res;
}

+ (XTHtmlColor *)forAttributeValueLink
{
	XTHtmlColor *res =  [XTHtmlColor forAttributeValue:PARAMD_COLOR_NAME_LINK];
	return res;
}

+ (XTHtmlColor *)forNSColor:(NSColor *)color
{
	XTHtmlColor *res = [XTHtmlColor new];
	res.resolvedColor = color;
	return res;
}

+ (XTHtmlColor *)forOsifcColor:(os_color_t)osifcColor
{
	XTHtmlColor *res;
	
	if (os_color_is_param(osifcColor)) {
		NSString *paramdName = [self paramdNameFromOsifcColor:(os_color_t)osifcColor];
		if (paramdName != nil) {
			res = [XTHtmlColor forAttributeValue:paramdName];
		} else {
			//TODO !!! adapt - what here?
			res = [XTHtmlColor forAttributeValue:@"black"];
		}
	} else {
		NSUInteger red8Bit = os_color_get_r(osifcColor);
		CGFloat red = [self rgbPctFromEightbitValue:red8Bit];
		NSUInteger green8Bit = os_color_get_g(osifcColor);
		CGFloat green = [self rgbPctFromEightbitValue:green8Bit];
		NSUInteger blue8Bit = os_color_get_b(osifcColor);
		CGFloat blue = [self rgbPctFromEightbitValue:blue8Bit];
		CGFloat alpha = 1.0;
		NSColor *nsColor = [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha];
		res = [XTHtmlColor forNSColor:nsColor];
	}
	
	return res;
}

- (void)setAttrValue:(NSString *)attrValue
{
	// no-op
}

- (NSString *)attrValue
{
	return _attrValue;
}

- (NSColor *)color
{
	NSColor *res = self.resolvedColor;
	if (res == nil) {
		res = [self resolveColor];
	}
	return res;
}

- (NSColor *)resolveColor
{
	NSColor *resolvedColor = [self resolveRgbColor];
	self.resolvedColor = resolvedColor;
	if (resolvedColor == nil) {
		resolvedColor = [self resolveStandardHtmlColor];
		self.resolvedColor = resolvedColor;
	}
	if (resolvedColor == nil) {
		resolvedColor = [self resolveParameterizedColor];
		// *don't* set self.resolvedColor
		_isForParameterized = (resolvedColor != nil);
	}
	return resolvedColor;
}

- (NSColor *)resolveRgbColor
{
	NSColor *res = nil;
	if (self.attrValue.length >= 1) {
		NSCharacterSet *hexDigitCharSet = [XTCharacterSets hexDigitCharSet];
		
		unichar ch = [self.attrValue characterAtIndex:0];
		if ((ch == '#') || [hexDigitCharSet characterIsMember:ch]) {
			NSString *hexValue;
			if (ch == '#') {
				hexValue = [self.attrValue substringFromIndex:1];
			} else {
				hexValue = self.attrValue;
			}
			NSUInteger uintValue;
			XTConverter *converter = [XTConverter converter];
			if ([converter hexToUInteger:hexValue uinteger:&uintValue]) {
				uintValue = uintValue & 0xFFFFFF;
				NSUInteger red8Bit = (uintValue & 0xFF0000) >> 16;
				CGFloat red = [XTHtmlColor rgbPctFromEightbitValue:red8Bit];
				NSUInteger green8Bit = (uintValue & 0xFF00) >> 8;
				CGFloat green = [XTHtmlColor rgbPctFromEightbitValue:green8Bit];
				NSUInteger blue8Bit = (uintValue & 0xFF);
				CGFloat blue = [XTHtmlColor rgbPctFromEightbitValue:blue8Bit];
				CGFloat alpha = 1.0;
				res = [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha];
				//res = [NSColor colorWithRed:red green:green blue:blue alpha:alpha];
			}
		}
	}
	return res;
}

+ (CGFloat)rgbPctFromEightbitValue:(NSUInteger)eightbitValue
{
	CGFloat res = eightbitValue / 255.0;
	return res;
}

- (NSColor *)resolveStandardHtmlColor
{
	NSColor *res = nil;
	if (self.attrValue.length >= 1) {
		NSString *key = [self.attrValue lowercaseString];
		res = [standardHtmlColorByName objectForKey:key];
	}
	return res;
}

- (NSColor *)resolveParameterizedColor
{
	XTPrefs *prefs = [XTPrefs prefs];
	
	NSColor *res = nil;
	if (self.attrValue.length >= 1) {
		NSString *key = [self.attrValue lowercaseString];
		if ([key isEqualToString:PARAMD_COLOR_NAME_TEXT]) {
			res = prefs.outputAreaTextColor.value;
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_BG]) {
			res = prefs.outputAreaBackgroundColor.value;
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_INPUT]) {
			res = prefs.inputTextColor.value;
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_STATUS_TEXT]) {
			res = prefs.statusLineTextColor.value;
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_STATUS_BG]) {
			res = prefs.statusLineBackgroundColor.value;
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_A_LINK]) {
			res = prefs.linksTextColor.value; //TODO !!! adapt: when sep. pref for "active link"
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_H_LINK]) {
			res = prefs.linksTextColor.value; //TODO !!! adapt: when sep. pref for "active link"
		} else if ([key isEqualToString:PARAMD_COLOR_NAME_LINK]) {
			res = prefs.linksTextColor.value;
		}
		//TODO !!! adapt: handle PARAMD_COLOR_NAME_TRANSPARENT
	}
	return res;
}

+ (NSString *)paramdNameFromOsifcColor:(os_color_t)osifcColor
{
	NSUInteger paramdVal = osifcColor & 0xFF000000;
	NSString *res = paramdColorNameByOsifcValue[[NSNumber numberWithUnsignedInteger:paramdVal]];
	return res;
}


@end
