//
// 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