// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package image

// TODO(nigeltao): Think about how floating-point color models work.

// All Colors can convert themselves, with a possible loss of precision, to 128-bit alpha-premultiplied RGBA.
type Color interface {
	RGBA() (r, g, b, a uint32);
}

// An RGBAColor represents a traditional 32-bit alpha-premultiplied color, having 8 bits for each of red, green, blue and alpha.
type RGBAColor struct {
	R, G, B, A uint8;
}

func (c RGBAColor) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R);
	r |= r<<8;
	r |= r<<16;
	g = uint32(c.G);
	g |= g<<8;
	g |= g<<16;
	b = uint32(c.B);
	b |= b<<8;
	b |= b<<16;
	a = uint32(c.A);
	a |= a<<8;
	a |= a<<16;
	return;
}

// An RGBA64Color represents a 64-bit alpha-premultiplied color, having 16 bits for each of red, green, blue and alpha.
type RGBA64Color struct {
	R, G, B, A uint16;
}

func (c RGBA64Color) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R);
	r |= r<<16;
	g = uint32(c.G);
	g |= g<<16;
	b = uint32(c.B);
	b |= b<<16;
	a = uint32(c.A);
	a |= a<<16;
	return;
}

// An NRGBAColor represents a non-alpha-premultiplied 32-bit color.
type NRGBAColor struct {
	R, G, B, A uint8;
}

func (c NRGBAColor) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R);
	r |= r<<8;
	r *= uint32(c.A);
	r /= 0xff;
	r |= r<<16;
	g = uint32(c.G);
	g |= g<<8;
	g *= uint32(c.A);
	g /= 0xff;
	g |= g<<16;
	b = uint32(c.B);
	b |= b<<8;
	b *= uint32(c.A);
	b /= 0xff;
	b |= b<<16;
	a = uint32(c.A);
	a |= a<<8;
	a |= a<<16;
	return;
}

// An NRGBA64Color represents a non-alpha-premultiplied 64-bit color, having 16 bits for each of red, green, blue and alpha.
type NRGBA64Color struct {
	R, G, B, A uint16;
}

func (c NRGBA64Color) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R);
	r *= uint32(c.A);
	r /= 0xffff;
	r |= r<<16;
	g = uint32(c.G);
	g *= uint32(c.A);
	g /= 0xffff;
	g |= g<<16;
	b = uint32(c.B);
	b *= uint32(c.A);
	b /= 0xffff;
	b |= b<<16;
	a = uint32(c.A);
	a |= a<<8;
	a |= a<<16;
	return;
}

// A ColorModel can convert foreign Colors, with a possible loss of precision, to a Color
// from its own color model.
type ColorModel interface {
	Convert(c Color) Color;
}

// The ColorModelFunc type is an adapter to allow the use of an ordinary
// color conversion function as a ColorModel.  If f is such a function,
// ColorModelFunc(f) is a ColorModel object that invokes f to implement
// the conversion.
type ColorModelFunc func(Color) Color

func (f ColorModelFunc) Convert(c Color) Color {
	return f(c);
}

func toRGBAColor(c Color) Color {
	if _, ok := c.(RGBAColor); ok {	// no-op conversion
		return c;
	}
	r, g, b, a := c.RGBA();
	return RGBAColor{uint8(r>>24), uint8(g>>24), uint8(b>>24), uint8(a>>24)};
}

func toRGBA64Color(c Color) Color {
	if _, ok := c.(RGBA64Color); ok {	// no-op conversion
		return c;
	}
	r, g, b, a := c.RGBA();
	return RGBA64Color{uint16(r>>16), uint16(g>>16), uint16(b>>16), uint16(a>>16)};
}

func toNRGBAColor(c Color) Color {
	if _, ok := c.(NRGBAColor); ok {	// no-op conversion
		return c;
	}
	r, g, b, a := c.RGBA();
	a >>= 16;
	if a == 0xffff {
		return NRGBAColor{uint8(r>>24), uint8(g>>24), uint8(b>>24), 0xff};
	}
	if a == 0 {
		return NRGBAColor{0, 0, 0, 0};
	}
	r >>= 16;
	g >>= 16;
	b >>= 16;
	// Since Color.RGBA returns a alpha-premultiplied color, we should have r <= a && g <= a && b <= a.
	r = (r*0xffff)/a;
	g = (g*0xffff)/a;
	b = (b*0xffff)/a;
	return NRGBAColor{uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8)};
}

func toNRGBA64Color(c Color) Color {
	if _, ok := c.(NRGBA64Color); ok {	// no-op conversion
		return c;
	}
	r, g, b, a := c.RGBA();
	a >>= 16;
	r >>= 16;
	g >>= 16;
	b >>= 16;
	if a == 0xffff {
		return NRGBA64Color{uint16(r), uint16(g), uint16(b), 0xffff};
	}
	if a == 0 {
		return NRGBA64Color{0, 0, 0, 0};
	}
	// Since Color.RGBA returns a alpha-premultiplied color, we should have r <= a && g <= a && b <= a.
	r = (r*0xffff)/a;
	g = (g*0xffff)/a;
	b = (b*0xffff)/a;
	return NRGBA64Color{uint16(r), uint16(g), uint16(b), uint16(a)};
}

// The ColorModel associated with RGBAColor.
var RGBAColorModel ColorModel = ColorModelFunc(toRGBAColor)

// The ColorModel associated with RGBA64Color.
var RGBA64ColorModel ColorModel = ColorModelFunc(toRGBA64Color)

// The ColorModel associated with NRGBAColor.
var NRGBAColorModel ColorModel = ColorModelFunc(toNRGBAColor)

// The ColorModel associated with NRGBA64Color.
var NRGBA64ColorModel ColorModel = ColorModelFunc(toNRGBA64Color)
