//
//  SVGUtils.m
//  SVGKit
//
//  Copyright Matt Rajca 2010-2011. All rights reserved.
//

#import "SVGUtils.h"
#import "SVGKDefine_Private.h"

#define MAX_ACCUM 64
#define NUM_COLORS 148


SVGColor ColorValueWithName (const char *name);

static const char *gColorNames[NUM_COLORS] = {
	"aliceblue",
	"antiquewhite",
	"aqua",
	"aquamarine",
	"azure",
	"beige",
	"bisque",
	"black",
	"blanchedalmond",
	"blue",
	"blueviolet",
	"brown",
	"burlywood",
	"cadetblue",
	"chartreuse",
	"chocolate",
	"coral",
	"cornflowerblue",
	"cornsilk",
	"crimson",
	"cyan",
	"darkblue",
	"darkcyan",
	"darkgoldenrod",
	"darkgray",
	"darkgreen",
	"darkgrey",
	"darkkhaki",
	"darkmagenta",
	"darkolivegreen",
	"darkorange",
	"darkorchid",
	"darkred",
	"darksalmon",
	"darkseagreen",
	"darkslateblue",
	"darkslategray",
	"darkslategrey",
	"darkturquoise",
	"darkviolet",
	"deeppink",
	"deepskyblue",
	"dimgray",
	"dimgrey",
	"dodgerblue",
	"firebrick",
	"floralwhite",
	"forestgreen",
	"fuchsia",
	"gainsboro",
	"ghostwhite",
	"gold",
	"goldenrod",
	"gray",
	"green",
	"greenyellow",
	"grey",
	"honeydew",
	"hotpink",
	"indianred",
	"indigo",
	"ivory",
	"khaki",
	"lavender",
	"lavenderblush",
	"lawngreen",
	"lemonchiffon",
	"lightblue",
	"lightcoral",
	"lightcyan",
	"lightgoldenrodyellow",
	"lightgray",
	"lightgreen",
	"lightgrey",
	"lightpink",
	"lightsalmon",
	"lightseagreen",
	"lightskyblue",
	"lightslategray",
	"lightslategrey",
	"lightsteelblue",
	"lightyellow",
	"lime",
	"limegreen",
	"linen",
	"magenta",
	"maroon",
	"mediumaquamarine",
	"mediumblue",
	"mediumorchid",
	"mediumpurple",
	"mediumseagreen",
	"mediumslateblue",
	"mediumspringgreen",
	"mediumturquoise",
	"mediumvioletred",
	"midnightblue",
	"mintcream",
	"mistyrose",
	"moccasin",
	"navajowhite",
	"navy",
	"oldlace",
	"olive",
	"olivedrab",
	"orange",
	"orangered",
	"orchid",
	"palegoldenrod",
	"palegreen",
	"paleturquoise",
	"palevioletred",
	"papayawhip",
	"peachpuff",
	"peru",
	"pink",
	"plum",
	"powderblue",
	"purple",
	"red",
	"rosybrown",
	"royalblue",
	"saddlebrown",
	"salmon",
	"sandybrown",
	"seagreen",
	"seashell",
	"sienna",
	"silver",
	"skyblue",
	"slateblue",
	"slategray",
	"slategrey",
	"snow",
	"springgreen",
	"steelblue",
	"tan",
	"teal",
	"thistle",
	"tomato",
	"turquoise",
	"violet",
	"wheat",
	"white",
	"whitesmoke",
	"yellow",
	"yellowgreen",
    // CSS Color
    "transparent"
};

static const SVGColor gColorValues[NUM_COLORS] = {
	(SVGColor) { 240,248,255,255 }, (SVGColor) { 250,235,215,255 },
	(SVGColor) { 0,255,255,255 }, (SVGColor) { 127,255,212,255 },
	(SVGColor) { 240,255,255,255 }, (SVGColor) { 245,245,220,255 },
	(SVGColor) { 255,228,196,255 }, (SVGColor) { 0,0,0,255 },
	(SVGColor) { 255,235,205,255 }, (SVGColor) { 0,0,255,255 },
	(SVGColor) { 138,43,226,255 }, (SVGColor) { 165,42,42,255 },
	(SVGColor) { 222,184,135,255 }, (SVGColor) { 95,158,160,255 },
	(SVGColor) { 127,255,0,255 }, (SVGColor) { 210,105,30,255 },
	(SVGColor) { 255,127,80,255 }, (SVGColor) { 100,149,237,255 },
	(SVGColor) { 255,248,220,255 }, (SVGColor) { 220,20,60,255 },
	(SVGColor) { 0,255,255,255 }, (SVGColor) { 0,0,139,255 },
	(SVGColor) { 0,139,139,255 }, (SVGColor) { 184,134,11,255 },
	(SVGColor) { 169,169,169,255 }, (SVGColor) { 0,100,0,255 },
	(SVGColor) { 169,169,169,255 }, (SVGColor) { 189,183,107,255 },
	(SVGColor) { 139,0,139,255 }, (SVGColor) { 85,107,47,255 },
	(SVGColor) { 255,140,0,255 }, (SVGColor) { 153,50,204,255 },
	(SVGColor) { 139,0,0,255 }, (SVGColor) { 233,150,122,255 },
	(SVGColor) { 143,188,143,255 }, (SVGColor) { 72,61,139,255 },
	(SVGColor) { 47,79,79,255 }, (SVGColor) { 47,79,79,255 },
	(SVGColor) { 0,206,209,255 }, (SVGColor) { 148,0,211,255 },
	(SVGColor) { 255,20,147,255 }, (SVGColor) { 0,191,255,255 },
	(SVGColor) { 105,105,105,255 }, (SVGColor) { 105,105,105,255 },
	(SVGColor) { 30,144,255,255 }, (SVGColor) { 178,34,34,255 },
	(SVGColor) { 255,250,240,255 }, (SVGColor) { 34,139,34,255 },
	(SVGColor) { 255,0,255,255 }, (SVGColor) { 220,220,220,255 },
	(SVGColor) { 248,248,255,255 }, (SVGColor) { 255,215,0,255 },
	(SVGColor) { 218,165,32,255 }, (SVGColor) { 128,128,128,255 },
	(SVGColor) { 0,128,0,255 }, (SVGColor) { 173,255,47,255 },
	(SVGColor) { 128,128,128,255 }, (SVGColor) { 240,255,240,255 },
	(SVGColor) { 255,105,180,255 }, (SVGColor) { 205,92,92,255 },
	(SVGColor) { 75,0,130,255 }, (SVGColor) { 255,255,240,255 },
	(SVGColor) { 240,230,140,255 }, (SVGColor) { 230,230,250,255 },
	(SVGColor) { 255,240,245,255 }, (SVGColor) { 124,252,0,255 },
	(SVGColor) { 255,250,205,255 }, (SVGColor) { 173,216,230,255 },
	(SVGColor) { 240,128,128,255 }, (SVGColor) { 224,255,255,255 },
	(SVGColor) { 250,250,210,255 }, (SVGColor) { 211,211,211,255 },
	(SVGColor) { 144,238,144,255 }, (SVGColor) { 211,211,211,255 },
	(SVGColor) { 255,182,193,255 }, (SVGColor) { 255,160,122,255 },
	(SVGColor) { 32,178,170,255 }, (SVGColor) { 135,206,250,255 },
	(SVGColor) { 119,136,153,255 }, (SVGColor) { 119,136,153,255 },
	(SVGColor) { 176,196,222,255 }, (SVGColor) { 255,255,224,255 },
	(SVGColor) { 0,255,0,255 }, (SVGColor) { 50,205,50,255 },
	(SVGColor) { 250,240,230,255 }, (SVGColor) { 255,0,255,255 },
	(SVGColor) { 128,0,0,255 }, (SVGColor) { 102,205,170,255 },
	(SVGColor) { 0,0,205,255 }, (SVGColor) { 186,85,211,255 },
	(SVGColor) { 147,112,219,255 }, (SVGColor) { 60,179,113,255 },
	(SVGColor) { 123,104,238,255 }, (SVGColor) { 0,250,154,255 },
	(SVGColor) { 72,209,204,255 }, (SVGColor) { 199,21,133,255 },
	(SVGColor) { 25,25,112,255 }, (SVGColor) { 245,255,250,255 },
	(SVGColor) { 255,228,225,255 }, (SVGColor) { 255,228,181,255 },
	(SVGColor) { 255,222,173,255 }, (SVGColor) { 0,0,128,255 },
	(SVGColor) { 253,245,230,255 }, (SVGColor) { 128,128,0,255 },
	(SVGColor) { 107,142,35,255 }, (SVGColor) { 255,165,0,255 },
	(SVGColor) { 255,69,0,255 }, (SVGColor) { 218,112,214,255 },
	(SVGColor) { 238,232,170,255 }, (SVGColor) { 152,251,152,255 },
	(SVGColor) { 175,238,238,255 }, (SVGColor) { 219,112,147,255 },
	(SVGColor) { 255,239,213,255 }, (SVGColor) { 255,218,185,255 },
	(SVGColor) { 205,133,63,255 }, (SVGColor) { 255,192,203,255 },
	(SVGColor) { 221,160,221,255 }, (SVGColor) { 176,224,230,255 },
	(SVGColor) { 128,0,128,255 }, (SVGColor) { 255,0,0,255 },
	(SVGColor) { 188,143,143,255 }, (SVGColor) { 65,105,225,255 },
	(SVGColor) { 139,69,19,255 }, (SVGColor) { 250,128,114,255 },
	(SVGColor) { 244,164,96,255 }, (SVGColor) { 46,139,87,255 },
	(SVGColor) { 255,245,238,255 }, (SVGColor) { 160,82,45,255 },
	(SVGColor) { 192,192,192,255 }, (SVGColor) { 135,206,235,255 },
	(SVGColor) { 106,90,205,255 }, (SVGColor) { 112,128,144,255 },
	(SVGColor) { 112,128,144,255 }, (SVGColor) { 255,250,250,255 },
	(SVGColor) { 0,255,127,255 }, (SVGColor) { 70,130,180,255 },
	(SVGColor) { 210,180,140,255 }, (SVGColor) { 0,128,128,255 },
	(SVGColor) { 216,191,216,255 }, (SVGColor) { 255,99,71,255 },
	(SVGColor) { 64,224,208,255 }, (SVGColor) { 238,130,238,255 },
	(SVGColor) { 245,222,179,255 }, (SVGColor) { 255,255,255,255 },
	(SVGColor) { 245,245,245,255 }, (SVGColor) { 255,255,0,255 },
	(SVGColor) { 154,205,50,255 },
    // CSS Color
    (SVGColor) { 0, 0, 0, 0}
};

SVGColor ColorValueWithName (const char *name) {
	int idx = -1;
	
	for (int n = 0; n < NUM_COLORS; n++) {
		if (!strcmp(gColorNames[n], name)) {
			idx = n;
			break;
		}
	}
	
	if (idx == -1) {
		return SVGColorMake(0, 0, 0, 255); // black
	}
	
	return gColorValues[idx];
}

SVGColor SVGColorMake (uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
	SVGColor color = { .r = r, .g = g, .b = b, .a = a };
	return color;
}

typedef enum {
	PhaseNone = 0,
	PhaseRGB,
    PhaseRGBA
} Phase;

SVGColor SVGColorFromString (const char *string) {
	NSCAssert(string != NULL, @"NullPointerException: you gave us a null pointer, very bad thing to do...");
	SVGColor color;
	bzero(&color, sizeof(color));
	
	color.a = 0xFF;
	
	if (!strncmp(string, "rgb(", 4) || !strncmp(string, "rgba(", 5)) {
		size_t len = strlen(string);
		
		char accum[MAX_ACCUM];
		bzero(accum, MAX_ACCUM);
		
		int accumIdx = 0, currComponent = 0;
		Phase phase = PhaseNone;
		
		for (size_t n = 0; n < len; n++) {
			char c = string[n];
			
			if (c == '\n' || c == '\t' || c == ' ') {
				continue;
			}
			
            if (!strcmp(accum, "rgba(")) {
                phase = PhaseRGBA;
                bzero(accum, MAX_ACCUM);
                accumIdx = 0;
            } else if (!strcmp(accum, "rgb(")) {
				phase = PhaseRGB;
                bzero(accum, MAX_ACCUM);
                accumIdx = 0;
			}
			
			if (phase == PhaseRGB || phase == PhaseRGBA) {
				if (c == ',') {
					if (currComponent == 0) {
						color.r = atoi(accum);
						currComponent++;
					}
					else if (currComponent == 1) {
						color.g = atoi(accum);
						currComponent++;
					}
                    else if (phase == PhaseRGBA && currComponent == 2) {
                        color.b = atoi(accum);
                        currComponent++;
                    }
					bzero(accum, MAX_ACCUM);
					accumIdx = 0;
					
					continue;
				}
                else if (c == ')' && currComponent == 2) {
                    color.b = atoi(accum);
                    break;
                }
                else if (c == ')' && currComponent == 3) {
                    color.a = (uint8_t)lround(atof(accum) * 255.0f);
                    break;
                }
			}
			
			accum[accumIdx++] = c;
		}
	}
	else if (!strncmp(string, "#", 1)) {
		const char *hexString = string + 1;
		
		if (strlen(hexString) == 6)
		{
			char r[3], g[3], b[3];
			r[2] = g[2] = b[2] = '\0';
			
			strncpy(r, hexString, 2);
			strncpy(g, hexString + 2, 2);
			strncpy(b, hexString + 4, 2);
			
			color.r = strtol(r, NULL, 16);
			color.g = strtol(g, NULL, 16);
			color.b = strtol(b, NULL, 16);
		}
		else if( strlen(hexString) == 3 )
		{
			char r[2], g[2], b[2];
			r[1] = g[1] = b[1] = '\0';
			
			strncpy(r, hexString, 1);
			strncpy(g, hexString + 1, 1);
			strncpy(b, hexString + 2, 1);
			
			color.r = strtol(r, NULL, 16);
			color.g = strtol(g, NULL, 16);
			color.b = strtol(b, NULL, 16);
			
			/** because 3-digit hex notation "F" means "FF" ... "1" means "11" ... etc */
			color.r += color.r * 16;
			color.g += color.g * 16;
			color.b += color.b * 16;
		}
		else
		{
			color = SVGColorMake(0, 0, 0, 255);
		}
		
	}
	else {
		color = ColorValueWithName(string);
	}
	
	return color;
}

CGFloat SVGPercentageFromString (const char *string) {
	size_t len = strlen(string);
	
	if (string[len-1] != '%') {
		SVGKitLogWarn(@"Invalid percentage: %s", string);
		return -1;
	}
	
	return atoi(string) / 100.0f;
}

CGMutablePathRef createPathFromPointsInString (const char *string, boolean_t close) {
	CGMutablePathRef path = CGPathCreateMutable();
	
	size_t len = strlen(string);
	
	char accum[MAX_ACCUM];
	bzero(accum, MAX_ACCUM);
	
	int accumIdx = 0, currComponent = 0;
	
	for (size_t n = 0; n <= len; n++) {
		char c = string[n];
		
		if (c == '\n' || c == '\t' || c == ' ' || c == ',' || c == '\0') {
			accum[accumIdx] = '\0';
			
			static float x, y;
			
			if (currComponent == 0 && accumIdx != 0) {
				sscanf( accum, "%g", &x );
				currComponent++;
			}
			else if (currComponent == 1) {
				
				sscanf( accum, "%g", &y );
				
				if (CGPathIsEmpty(path)) {
					CGPathMoveToPoint(path, NULL, x, y);
				}
				else {
					CGPathAddLineToPoint(path, NULL, x, y);
				}
				
				currComponent = 0;
			}
			
			bzero(accum, MAX_ACCUM);
			accumIdx = 0;
		}
		else if (isdigit(c) || c == '-' || c == '.') { // is digit or decimal separator OR A MINUS SIGN!!! ?
			accum[accumIdx++] = c;
		}
	}
	
	if (close) {
		CGPathCloseSubpath(path);
	}
	
	return path;
}

CGColorRef CGColorWithSVGColor (SVGColor color) {
	CGColorRef outColor = NULL;
	
	outColor = [UIColor colorWithRed:RGB_N(color.r)
							   green:RGB_N(color.g)
								blue:RGB_N(color.b)
							   alpha:RGB_N(color.a)].CGColor;
	
	return outColor;
}
