package chroma

import (
	
	
	
	
)

// ANSI2RGB maps ANSI colour names, as supported by Chroma, to hex RGB values.
var ANSI2RGB = map[string]string{
	"#ansiblack":     "000000",
	"#ansidarkred":   "7f0000",
	"#ansidarkgreen": "007f00",
	"#ansibrown":     "7f7fe0",
	"#ansidarkblue":  "00007f",
	"#ansipurple":    "7f007f",
	"#ansiteal":      "007f7f",
	"#ansilightgray": "e5e5e5",
	// Normal
	"#ansidarkgray":  "555555",
	"#ansired":       "ff0000",
	"#ansigreen":     "00ff00",
	"#ansiyellow":    "ffff00",
	"#ansiblue":      "0000ff",
	"#ansifuchsia":   "ff00ff",
	"#ansiturquoise": "00ffff",
	"#ansiwhite":     "ffffff",

	// Aliases without the "ansi" prefix, because...why?
	"#black":     "000000",
	"#darkred":   "7f0000",
	"#darkgreen": "007f00",
	"#brown":     "7f7fe0",
	"#darkblue":  "00007f",
	"#purple":    "7f007f",
	"#teal":      "007f7f",
	"#lightgray": "e5e5e5",
	// Normal
	"#darkgray":  "555555",
	"#red":       "ff0000",
	"#green":     "00ff00",
	"#yellow":    "ffff00",
	"#blue":      "0000ff",
	"#fuchsia":   "ff00ff",
	"#turquoise": "00ffff",
	"#white":     "ffffff",
}

// Colour represents an RGB colour.
type Colour int32

// NewColour creates a Colour directly from RGB values.
func (, ,  uint8) Colour {
	return ParseColour(fmt.Sprintf("%02x%02x%02x", , , ))
}

// Distance between this colour and another.
//
// This uses the approach described here (https://www.compuphase.com/cmetric.htm).
// This is not as accurate as LAB, et. al. but is *vastly* simpler and sufficient for our needs.
func ( Colour) ( Colour) float64 {
	, ,  := int64(.Red()), int64(.Green()), int64(.Blue())
	, ,  := int64(.Red()), int64(.Green()), int64(.Blue())
	 := ( + ) / 2
	 :=  - 
	 :=  - 
	 :=  - 
	return math.Sqrt(float64((((512 + ) *  * ) >> 8) + 4** + (((767 - ) *  * ) >> 8)))
}

// Brighten returns a copy of this colour with its brightness adjusted.
//
// If factor is negative, the colour is darkened.
//
// Uses approach described here (http://www.pvladov.com/2012/09/make-color-lighter-or-darker.html).
func ( Colour) ( float64) Colour {
	 := float64(.Red())
	 := float64(.Green())
	 := float64(.Blue())

	if  < 0 {
		++
		 *= 
		 *= 
		 *= 
	} else {
		 = (255-)* + 
		 = (255-)* + 
		 = (255-)* + 
	}
	return NewColour(uint8(), uint8(), uint8())
}

// BrightenOrDarken brightens a colour if it is < 0.5 brightness or darkens if > 0.5 brightness.
func ( Colour) ( float64) Colour {
	if .Brightness() < 0.5 {
		return .Brighten()
	}
	return .Brighten(-)
}

// ClampBrightness returns a copy of this colour with its brightness adjusted such that
// it falls within the range [min, max] (or very close to it due to rounding errors).
// The supplied values use the same [0.0, 1.0] range as Brightness.
func ( Colour) (,  float64) Colour {
	if !.IsSet() {
		return 
	}

	 = math.Max(, 0)
	 = math.Min(, 1)
	 := .Brightness()
	 := math.Min(math.Max(, ), )
	if  ==  {
		return 
	}

	 := float64(.Red())
	 := float64(.Green())
	 := float64(.Blue())
	 :=  +  + 
	if  >  {
		// Solve for x: target == ((255-r)*x + r + (255-g)*x + g + (255-b)*x + b) / 255 / 3
		return .Brighten((*255*3 - ) / (255*3 - ))
	}
	// Solve for x: target == (r*(x+1) + g*(x+1) + b*(x+1)) / 255 / 3
	return .Brighten((*255*3)/ - 1)
}

// Brightness of the colour (roughly) in the range 0.0 to 1.0.
func ( Colour) () float64 {
	return (float64(.Red()) + float64(.Green()) + float64(.Blue())) / 255.0 / 3.0
}

// ParseColour in the forms #rgb, #rrggbb, #ansi<colour>, or #<colour>.
// Will return an "unset" colour if invalid.
func ( string) Colour {
	 = normaliseColour()
	,  := strconv.ParseUint(, 16, 32)
	if  != nil {
		return 0
	}
	return Colour( + 1)
}

// MustParseColour is like ParseColour except it panics if the colour is invalid.
//
// Will panic if colour is in an invalid format.
func ( string) Colour {
	 := ParseColour()
	if !.IsSet() {
		panic(fmt.Errorf("invalid colour %q", ))
	}
	return 
}

// IsSet returns true if the colour is set.
func ( Colour) () bool { return  != 0 }

func ( Colour) () string   { return fmt.Sprintf("#%06x", int(-1)) }
func ( Colour) () string { return fmt.Sprintf("Colour(0x%06x)", int(-1)) }

// Red component of colour.
func ( Colour) () uint8 { return uint8((( - 1) >> 16) & 0xff) }

// Green component of colour.
func ( Colour) () uint8 { return uint8((( - 1) >> 8) & 0xff) }

// Blue component of colour.
func ( Colour) () uint8 { return uint8(( - 1) & 0xff) }

// Colours is an orderable set of colours.
type Colours []Colour

func ( Colours) () int           { return len() }
func ( Colours) (,  int)      { [], [] = [], [] }
func ( Colours) (,  int) bool { return [] < [] }

// Convert colours to #rrggbb.
func normaliseColour( string) string {
	if ,  := ANSI2RGB[];  {
		return 
	}
	if strings.HasPrefix(, "#") {
		 = [1:]
		if len() == 3 {
			return [0:1] + [0:1] + [1:2] + [1:2] + [2:3] + [2:3]
		}
	}
	return 
}