//+build withimagecrush

package thumbnail

import (
	"bytes"
	"fmt"
	"image"
	"image/color"
	"image/png"

	"code.ivysaur.me/imagequant"
)

func goImageToRgba32(im image.Image) []byte {
	w := im.Bounds().Max.X
	h := im.Bounds().Max.Y
	ret := make([]byte, w*h*4)

	p := 0

	for y := 0; y < h; y += 1 {
		for x := 0; x < w; x += 1 {
			r16, g16, b16, a16 := im.At(x, y).RGBA() // Each value ranges within [0, 0xffff]

			ret[p+0] = uint8(r16 >> 8)
			ret[p+1] = uint8(g16 >> 8)
			ret[p+2] = uint8(b16 >> 8)
			ret[p+3] = uint8(a16 >> 8)
			p += 4
		}
	}

	return ret
}

func rgb8PaletteToGoImage(w, h int, rgb8data []byte, pal color.Palette) image.Image {
	rect := image.Rectangle{
		Max: image.Point{
			X: w,
			Y: h,
		},
	}

	ret := image.NewPaletted(rect, pal)

	for y := 0; y < h; y += 1 {
		for x := 0; x < w; x += 1 {
			ret.SetColorIndex(x, y, rgb8data[y*h+x])
		}
	}

	return ret
}

func crushFast(img image.Image) ([]byte, error) {
	return crush(img, imagequant.SPEED_FASTEST)
}

func crush(img image.Image, speed int) ([]byte, error) {

	width := img.Bounds().Max.X
	height := img.Bounds().Max.Y

	attr, err := imagequant.NewAttributes()
	if err != nil {
		return nil, fmt.Errorf("NewAttributes: %s", err.Error())
	}
	defer attr.Release()

	err = attr.SetSpeed(speed)
	if err != nil {
		return nil, fmt.Errorf("SetSpeed: %s", err.Error())
	}

	rgba32data := goImageToRgba32(img)

	iqm, err := imagequant.NewImage(attr, string(rgba32data), width, height, 0)
	if err != nil {
		return nil, fmt.Errorf("NewImage: %s", err.Error())
	}
	defer iqm.Release()

	res, err := iqm.Quantize(attr)
	if err != nil {
		return nil, fmt.Errorf("Quantize: %s", err.Error())
	}
	defer res.Release()

	rgb8data, err := res.WriteRemappedImage()
	if err != nil {
		return nil, fmt.Errorf("WriteRemappedImage: %s", err.Error())
	}

	im2 := rgb8PaletteToGoImage(res.GetImageWidth(), res.GetImageHeight(), rgb8data, res.GetPalette())

	buff := bytes.Buffer{}

	penc := png.Encoder{
		CompressionLevel: png.BestCompression,
	}
	err = penc.Encode(&buff, im2)
	if err != nil {
		return nil, fmt.Errorf("png.Encode: %s", err.Error())
	}

	return buff.Bytes(), nil
}
