package goeffects

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

import (
	"image"
	"math"
	"runtime"
)

type sobel struct {
	threshold int
	invert    bool
}

// NewSobel the input image should be a grayscale image, the output will be a version of
// the input image with the Sobel edge detector applied to it. A value of -1 for threshold
// will return an image whos rgb values are the sobel intensity values, if 0 <= threshold <= 255
// then the rgb values will be 255 if the intensity is >= threshold and 0 if the intensity
// is < threshold
func NewSobel(threshold int, invert bool) Effect {
	return &sobel{
		threshold: threshold,
		invert:    invert,
	}
}

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

	sobelX := [][]int{
		{-1, 0, 1},
		{-2, 0, 2},
		{-1, 0, 1},
	}
	sobelY := [][]int{
		{-1, -2, -1},
		{0, 0, 0},
		{1, 2, 1},
	}

	pf := func(ri, x, y, offset, inStride int, inPix, outPix []uint8) {
		var px, py int
		for dy := -1; dy <= 1; dy++ {
			for dx := -1; dx <= 1; dx++ {
				pOffset := offset + (dx*4 + dy*inStride)
				r := int(inPix[pOffset])
				px += sobelX[dx+1][dy+1] * r
				py += sobelY[dx+1][dy+1] * r
			}
		}

		val := uint8(math.Sqrt(float64(px*px + py*py)))
		if s.threshold != -1 {
			if val >= uint8(s.threshold) {
				val = 255
			} else {
				val = 0
			}
		}

		if s.invert {
			val = 255 - val
		}
		outPix[offset] = val
		outPix[offset+1] = val
		outPix[offset+2] = val
		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 + 1,
			Y:      img.Bounds.Y + 1,
			Width:  img.Bounds.Width - 2,
			Height: img.Bounds.Height - 2,
		},
	}

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