package termutil

import (
	"fmt"
	"image/color"
	"strconv"
)

type Colour uint8

// See https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
const (
	ColourBlack Colour = iota
	ColourRed
	ColourGreen
	ColourYellow
	ColourBlue
	ColourMagenta
	ColourCyan
	ColourWhite
	ColourBrightBlack
	ColourBrightRed
	ColourBrightGreen
	ColourBrightYellow
	ColourBrightBlue
	ColourBrightMagenta
	ColourBrightCyan
	ColourBrightWhite
	ColourBackground
	ColourForeground
	ColourSelectionBackground
	ColourSelectionForeground
	ColourCursorForeground
	ColourCursorBackground
)

type Theme struct {
	alpha     uint8
	colourMap map[Colour]color.Color
}

var (
	map4Bit = map[uint8]Colour{
		30:  ColourBlack,
		31:  ColourRed,
		32:  ColourGreen,
		33:  ColourYellow,
		34:  ColourBlue,
		35:  ColourMagenta,
		36:  ColourCyan,
		37:  ColourWhite,
		90:  ColourBrightBlack,
		91:  ColourBrightRed,
		92:  ColourBrightGreen,
		93:  ColourBrightYellow,
		94:  ColourBrightBlue,
		95:  ColourBrightMagenta,
		96:  ColourBrightCyan,
		97:  ColourBrightWhite,
		40:  ColourBlack,
		41:  ColourRed,
		42:  ColourGreen,
		43:  ColourYellow,
		44:  ColourBlue,
		45:  ColourMagenta,
		46:  ColourCyan,
		47:  ColourWhite,
		100: ColourBrightBlack,
		101: ColourBrightRed,
		102: ColourBrightGreen,
		103: ColourBrightYellow,
		104: ColourBrightBlue,
		105: ColourBrightMagenta,
		106: ColourBrightCyan,
		107: ColourBrightWhite,
	}
)

func (t *Theme) ColourFrom4Bit(code uint8) color.Color {
	colour, ok := map4Bit[code]
	if !ok {
		return color.Black
	}
	return t.colourMap[colour]
}

func (t *Theme) DefaultBackground() color.Color {
	c, ok := t.colourMap[ColourBackground]
	if !ok {
		return color.RGBA{0, 0, 0, t.alpha}
	}
	return c
}

func (t *Theme) DefaultForeground() color.Color {
	c, ok := t.colourMap[ColourForeground]
	if !ok {
		return color.RGBA{255, 255, 255, t.alpha}
	}
	return c
}

func (t *Theme) SelectionBackground() color.Color {
	c, ok := t.colourMap[ColourSelectionBackground]
	if !ok {
		return color.RGBA{0, 0, 0, t.alpha}
	}
	return c
}

func (t *Theme) SelectionForeground() color.Color {
	c, ok := t.colourMap[ColourSelectionForeground]
	if !ok {
		return color.RGBA{255, 255, 255, t.alpha}
	}
	return c
}

func (t *Theme) CursorBackground() color.Color {
	c, ok := t.colourMap[ColourCursorBackground]
	if !ok {
		return color.RGBA{255, 255, 255, t.alpha}
	}
	return c
}

func (t *Theme) CursorForeground() color.Color {
	c, ok := t.colourMap[ColourCursorForeground]
	if !ok {
		return color.RGBA{0, 0, 0, t.alpha}
	}
	return c
}

func (t *Theme) ColourFrom8Bit(n string) (color.Color, error) {

	index, err := strconv.Atoi(n)
	if err != nil {
		return nil, err
	}

	if index < 16 {
		return t.colourMap[Colour(index)], nil
	}

	if index >= 232 {
		c := ((index - 232) * 0xff) / 0x18
		return color.RGBA{
			R: byte(c),
			G: byte(c),
			B: byte(c),
			A: t.alpha,
		}, nil
	}

	var colour color.RGBA
	colour.A = t.alpha
	indexR := ((index - 16) / 36)
	if indexR > 0 {
		colour.R = uint8(55 + indexR*40)
	}
	indexG := (((index - 16) % 36) / 6)
	if indexG > 0 {
		colour.G = uint8(55 + indexG*40)
	}
	indexB := ((index - 16) % 6)
	if indexB > 0 {
		colour.B = uint8(55 + indexB*40)
	}

	return colour, nil
}

func (t *Theme) ColourFrom24Bit(r, g, b string) (color.Color, error) {
	ri, err := strconv.Atoi(r)
	if err != nil {
		return nil, err
	}
	gi, err := strconv.Atoi(g)
	if err != nil {
		return nil, err
	}
	bi, err := strconv.Atoi(b)
	if err != nil {
		return nil, err
	}
	return color.RGBA{
		R: byte(ri),
		G: byte(gi),
		B: byte(bi),
		A: t.alpha,
	}, nil
}

func (t *Theme) ColourFromAnsi(ansi []string, bg bool) (color.Color, error) {

	if len(ansi) == 0 {
		return nil, fmt.Errorf("invalid ansi colour code")
	}

	switch ansi[0] {
	case "2":
		if len(ansi) != 4 {
			return nil, fmt.Errorf("invalid 24-bit ansi colour code")
		}
		return t.ColourFrom24Bit(ansi[1], ansi[2], ansi[3])
	case "5":
		if len(ansi) != 2 {
			return nil, fmt.Errorf("invalid 8-bit ansi colour code")
		}
		return t.ColourFrom8Bit(ansi[1])
	default:
		return nil, fmt.Errorf("invalid ansi colour code")
	}
}
