package goeffects

// Package copied from https://github.com/markdaws/go-effects

import "image"
import "runtime"

// GSAlgo the type of algorithm to use when converting an image to it's grayscale equivalent
type GSAlgo int

const (
	// GSLIGHTNESS is the average of the min and max r,g,b value
	GSLIGHTNESS GSAlgo = iota

	// GSAVERAGE is the average of the r,g,b values of each pixel
	GSAVERAGE

	// GSLUMINOSITY used a weighting for r,g,b based on how the human eye perceives colors
	GSLUMINOSITY
)

type grayscale struct {
	algo GSAlgo
}

func (gs *grayscale) Apply(img *Image, numRoutines int) (*Image, error) {
	if numRoutines == 0 {
		numRoutines = runtime.GOMAXPROCS(0)
	}

	pf := func(ri, x, y, offset, inStride int, inPix, outPix []uint8) {
		var r, g, b uint8 = inPix[offset], inPix[offset+1], inPix[offset+2]
		switch gs.algo {
		case GSLIGHTNESS:
			maximum := float64(max(max(r, g), b))
			minimum := float64(max(min(r, g), b))
			r = uint8(maximum + minimum/2)
			g = r
			b = r
		case GSAVERAGE:
			r = (r + g + b) / 3
			g = r
			b = r
		case GSLUMINOSITY:
			r = uint8(0.21*float64(r) + 0.72*float64(g) + 0.07*float64(b))
			g = r
			b = r
		}
		outPix[offset] = r
		outPix[offset+1] = g
		outPix[offset+2] = b
		outPix[offset+3] = 255
	}

	out := &Image{
		img: image.NewRGBA(image.Rectangle{
			Min: image.Point{X: 0, Y: 0},
			Max: image.Point{X: img.Width, Y: img.Height},
		}),
		Width:  img.Width,
		Height: img.Height,
		Bounds: Rect{
			X:      img.Bounds.X,
			Y:      img.Bounds.Y,
			Width:  img.Bounds.Width,
			Height: img.Bounds.Height,
		},
	}

	runParallel(numRoutines, img, out.Bounds, out, pf, 0)
	return out, nil
}

// NewGrayscale renders the input image as a grayscale image. numRoutines specifies how many
// goroutines should be used to process the image in parallel, use 0 to let the library decide
func NewGrayscale(algo GSAlgo) Effect {
	return &grayscale{algo: algo}
}
