module gx

pub const black = Color{
	r: 0
	g: 0
	b: 0
}
pub const gray = Color{
	r: 128
	g: 128
	b: 128
}
pub const white = Color{
	r: 255
	g: 255
	b: 255
}
pub const red = Color{
	r: 255
	g: 0
	b: 0
}
pub const green = Color{
	r: 0
	g: 255
	b: 0
}
pub const blue = Color{
	r: 0
	g: 0
	b: 255
}
pub const yellow = Color{
	r: 255
	g: 255
	b: 0
}
pub const magenta = Color{
	r: 255
	g: 0
	b: 255
}
pub const cyan = Color{
	r: 0
	g: 255
	b: 255
}
pub const orange = Color{
	r: 255
	g: 165
	b: 0
}
pub const purple = Color{
	r: 128
	g: 0
	b: 128
}
pub const indigo = Color{
	r: 75
	g: 0
	b: 130
}
pub const pink = Color{
	r: 255
	g: 192
	b: 203
}
pub const violet = Color{
	r: 238
	g: 130
	b: 238
}
pub const dark_blue = Color{
	r: 0
	g: 0
	b: 139
}
pub const dark_gray = Color{
	r: 169
	g: 169
	b: 169
}
pub const dark_green = Color{
	r: 0
	g: 100
	b: 0
}
pub const dark_red = Color{
	r: 139
	g: 0
	b: 0
}
pub const light_blue = Color{
	r: 173
	g: 216
	b: 230
}
pub const light_gray = Color{
	r: 211
	g: 211
	b: 211
}
pub const light_green = Color{
	r: 144
	g: 238
	b: 144
}
pub const light_red = Color{
	r: 255
	g: 204
	b: 203
}

// Color represents a 32 bit color value in sRGB format
pub struct Color {
pub mut:
	r u8
	g u8
	b u8
	a u8 = 255
}

// hex takes in a 32 bit integer and splits it into 4 byte values
/*
pub fn hex(color int) Color {
	return Color{
		r: u8((color >> 24) & 0xFF)
		g: u8((color >> 16) & 0xFF)
		b: u8((color >> 8) & 0xFF)
		a: u8(color & 0xFF)
	}
}
*/
pub fn hex(color int) Color {
	return Color{
		r: u8((color >> 16) & 0xFF)
		g: u8((color >> 8) & 0xFF)
		b: u8((color >> 0) & 0xFF)
	}
}

// rgb builds a Color instance from given r, g, b values
pub fn rgb(r u8, g u8, b u8) Color {
	return Color{
		r: r
		g: g
		b: b
	}
}

// rgba builds a Color instance from given r, g, b, a values
pub fn rgba(r u8, g u8, b u8, a u8) Color {
	return Color{
		r: r
		g: g
		b: b
		a: a
	}
}

// + adds `b` to `a`, with a maximum value of 255 for each channel
pub fn (a Color) + (b Color) Color {
	mut na := int(a.a) + b.a
	mut nr := int(a.r) + b.r
	mut ng := int(a.g) + b.g
	mut nb := int(a.b) + b.b
	if na > 255 {
		na = 255
	}
	if nr > 255 {
		nr = 255
	}
	if ng > 255 {
		ng = 255
	}
	if nb > 255 {
		nb = 255
	}
	return Color{
		r: u8(nr)
		g: u8(ng)
		b: u8(nb)
		a: u8(na)
	}
}

// - subtracts `b` from `a`, with a minimum value of 0 for each channel
pub fn (a Color) - (b Color) Color {
	mut na := if a.a > b.a { a.a } else { b.a }
	mut nr := int(a.r) - b.r
	mut ng := int(a.g) - b.g
	mut nb := int(a.b) - b.b
	if na < 0 {
		na = 0
	}
	if nr < 0 {
		nr = 0
	}
	if ng < 0 {
		ng = 0
	}
	if nb < 0 {
		nb = 0
	}
	return Color{
		r: u8(nr)
		g: u8(ng)
		b: u8(nb)
		a: u8(na)
	}
}

// * multiplies Color `c` and `c2` keeping channel values in [0, 255] range
pub fn (c Color) * (c2 Color) Color {
	return Color{
		r: c.r * c2.r
		g: c.g * c2.g
		b: c.b * c2.b
		a: c.a * c2.a
	}
}

// / divides `c` by `c2` and converts each channel's value to u8(int)
pub fn (c Color) / (c2 Color) Color {
	return Color{
		r: c.r / c2.r
		g: c.g / c2.g
		b: c.b / c2.b
		a: c.a / c2.a
	}
}

// over implements an `a` over `b` operation.
// see https://keithp.com/~keithp/porterduff/p253-porter.pdf
pub fn (a Color) over(b Color) Color {
	aa := f32(a.a) / 255
	ab := f32(b.a) / 255
	ar := aa + ab * (1 - aa)
	//
	rr := (f32(a.r) * aa + f32(b.r) * ab * (1 - aa)) / ar
	gr := (f32(a.g) * aa + f32(b.g) * ab * (1 - aa)) / ar
	br := (f32(a.b) * aa + f32(b.b) * ab * (1 - aa)) / ar
	return Color{
		r: u8(rr)
		g: u8(gr)
		b: u8(br)
		a: u8(ar * 255)
	}
}

// eq checks if color `c` and `c2` are equal in every channel
pub fn (c Color) eq(c2 Color) bool {
	return c.r == c2.r && c.g == c2.g && c.b == c2.b && c.a == c2.a
}

// str returns a string representation of the Color `c`
pub fn (c Color) str() string {
	return 'Color{${c.r}, ${c.g}, ${c.b}, ${c.a}}'
}

// rgba8 converts a color value to an int in the RGBA8 order.
// see https://developer.apple.com/documentation/coreimage/ciformat
@[inline]
pub fn (c Color) rgba8() int {
	return int(u32(c.r) << 24 | u32(c.g) << 16 | u32(c.b) << 8 | u32(c.a))
}

// bgra8 converts a color value to an int in the BGRA8 order.
// see https://developer.apple.com/documentation/coreimage/ciformat
@[inline]
pub fn (c Color) bgra8() int {
	return int(u32(c.b) << 24 | u32(c.g) << 16 | u32(c.r) << 8 | u32(c.a))
}

// abgr8 converts a color value to an int in the ABGR8 order.
// see https://developer.apple.com/documentation/coreimage/ciformat
@[inline]
pub fn (c Color) abgr8() int {
	return int(u32(c.a) << 24 | u32(c.b) << 16 | u32(c.g) << 8 | u32(c.r))
}

const string_colors = {
	'blue':        blue
	'red':         red
	'green':       green
	'yellow':      yellow
	'orange':      orange
	'purple':      purple
	'black':       black
	'gray':        gray
	'indigo':      indigo
	'pink':        pink
	'violet':      violet
	'white':       white
	'dark_blue':   dark_blue
	'dark_gray':   dark_gray
	'dark_green':  dark_green
	'dark_red':    dark_red
	'light_blue':  light_blue
	'light_gray':  light_gray
	'light_green': light_green
	'light_red':   light_red
}

// color_from_string returns a Color, corresponding to the given string
// or black Color if string is not found in lookup table, or a hex color if starting with #
pub fn color_from_string(s string) Color {
	if s.starts_with('#') {
		mut hex_str := '0x' + s[1..]
		return hex(hex_str.int())
	} else {
		return gx.string_colors[s]
	}
}

// to_css_string returns a CSS compatible string e.g. `rgba(10,11,12,13)` of the color `c`.
pub fn (c Color) to_css_string() string {
	return 'rgba(${c.r},${c.g},${c.b},${c.a})'
}
