package ui

import (
	"image"

	"github.com/nfnt/resize"
	"opslag.de/schobers/geom"
)

func ScaleTexture(render Renderer, texture Texture, scale float32) Texture {
	w := uint(texture.Width() * scale)
	if w == 0 {
		return nil
	}
	source, ok := texture.(ImageSource)
	if !ok {
		return nil
	}
	image, err := source.CreateImage()
	if err != nil {
		return nil
	}
	scaled := resize.Resize(w, 0, image, resize.Bilinear)
	res, err := render.CreateTextureGo(scaled, false)
	if err != nil {
		return nil
	}
	return res
}

type Textures struct {
	render   Renderer
	textures map[string]Texture
	scaled   map[Texture]ScaledTextures
}

func NewTextures(render Renderer) *Textures {
	return &Textures{render, map[string]Texture{}, map[Texture]ScaledTextures{}}
}

func (t *Textures) AddTexture(name string, texture Texture) {
	curr := t.textures[name]
	if curr != nil {
		curr.Destroy()
	}
	t.textures[name] = texture
}

func (t *Textures) createTexture(name string, create func() (Texture, error)) (Texture, error) {
	texture, err := create()
	if err != nil {
		return nil, err
	}
	t.AddTexture(name, texture)
	return texture, nil
}

func (t *Textures) CreateTexture(name string, source ImageSource) (Texture, error) {
	return t.createTexture(name, func() (Texture, error) {
		return t.render.CreateTexture(source)
	})
}

func (t *Textures) CreateTextureGo(name string, im image.Image, source bool) (Texture, error) {
	return t.createTexture(name, func() (Texture, error) {
		return t.render.CreateTextureGo(im, source)
	})
}

func (t *Textures) CreateTexturePath(name string, path string, source bool) (Texture, error) {
	return t.createTexture(name, func() (Texture, error) {
		return t.render.CreateTexturePath(path, source)
	})
}

func (t *Textures) Destroy() {
	for _, texture := range t.textures {
		texture.Destroy()
	}
	t.textures = nil
	for _, texture := range t.scaled {
		texture.Destroy()
	}
	t.scaled = nil
}

func (t *Textures) Texture(name string) Texture {
	texture, ok := t.textures[name]
	if ok {
		return texture
	}
	return nil
}

func (t *Textures) Scaled(texture Texture, scale float32) Texture {
	if scale <= 0 {
		return nil
	}
	if scale == 1 {
		return texture
	}
	textures := t.scaled[texture]
	if textures == nil {
		textures = make(ScaledTextures)
	} else {
		scaled := textures[scale]
		if scaled != nil {
			return scaled
		}
	}
	scaled := ScaleTexture(t.render, texture, scale)
	if scaled == nil {
		return nil
	}
	textures[scale] = scaled
	t.scaled[texture] = textures
	return scaled
}

func (t *Textures) ScaledHeight(texture Texture, height float32) (Texture, float32) {
	scale := height / texture.Height()
	if geom.IsNaN32(scale) {
		return nil, 0
	}
	return t.Scaled(texture, scale), scale
}

func (t *Textures) ScaledByName(name string, scale float32) Texture {
	texture := t.Texture(name)
	if texture == nil {
		return nil
	}
	return t.Scaled(texture, scale)
}

type ScaledTextures map[float32]Texture

func (t ScaledTextures) Destroy() {
	for _, texture := range t {
		texture.Destroy()
	}
}