package main

import (
	"image"
	"image/color"
	_ "image/gif"
	"image/jpeg"
	_ "image/png"
	"io"
)

func init() {
	thumbnailers = append(thumbnailers, &imageThumbnailer{})
}

type imageThumbnailer struct{}

func (it *imageThumbnailer) CanHandle(ext string) bool {
	switch ext {
	case ".jpg", ".jpeg", ".png", ".gif":
		return true
	}
	return false
}

func (it *imageThumbnailer) Generate(src io.Reader, dst io.Writer, width int) error {
	img, _, err := image.Decode(src)
	if err != nil {
		return err
	}
	return jpeg.Encode(dst, resizeBox(img, width), &jpeg.Options{Quality: 80})
}

// resizeBox downsamples src to the requested width using a box filter.
// Aspect ratio is preserved. Upscaling is a no-op (returns src unchanged).
// Each source pixel is visited exactly once; alpha is discarded.
func resizeBox(src image.Image, width int) image.Image {
	b := src.Bounds()
	srcW, srcH := b.Dx(), b.Dy()
	if srcW <= width {
		return src
	}
	dstW := width
	dstH := srcH * width / srcW
	if dstH < 1 {
		dstH = 1
	}
	dst := image.NewRGBA(image.Rect(0, 0, dstW, dstH))

	for y := 0; y < dstH; y++ {
		sy0 := y * srcH / dstH
		sy1 := (y + 1) * srcH / dstH
		if sy1 == sy0 {
			sy1 = sy0 + 1
		}
		for x := 0; x < dstW; x++ {
			sx0 := x * srcW / dstW
			sx1 := (x + 1) * srcW / dstW
			if sx1 == sx0 {
				sx1 = sx0 + 1
			}
			var r, g, bl, n uint64
			for sy := sy0; sy < sy1; sy++ {
				for sx := sx0; sx < sx1; sx++ {
					sr, sg, sb, _ := src.At(b.Min.X+sx, b.Min.Y+sy).RGBA()
					r += uint64(sr >> 8)
					g += uint64(sg >> 8)
					bl += uint64(sb >> 8)
					n++
				}
			}
			dst.SetRGBA(x, y, color.RGBA{
				R: uint8(r / n),
				G: uint8(g / n),
				B: uint8(bl / n),
				A: 255,
			})
		}
	}
	return dst
}
