package goeffects

// goeffects.go provides functions to interface with go-effects
// Package copied from https://github.com/markdaws/go-effects/

import "seekia/internal/helpers"
import "seekia/internal/imagery"

import "image"
import "image/draw"
import "errors"


func ApplyCartoonEffect(inputImage image.Image, effectStrength int)(image.Image, error){

	if (inputImage == nil){
		return nil, errors.New("ApplyCartoonEffect called with nil image.")
	}

	if (effectStrength < 0 || effectStrength > 100){
		return nil, errors.New("ApplyCartoonEffect called with invalid effectStrength.")	
	}

	if (effectStrength == 0) {
		return inputImage, nil
	}
	

	blurKernelSize, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 1, 3)
	if (err != nil) { return nil, err }
	if (blurKernelSize % 2 == 0){
		blurKernelSize += 1
	}

	edgeThreshold, err := helpers.ScaleIntProportionally(false, effectStrength, 0, 100, 5, 200)
	if (err != nil) { return nil, err }

	oilFilterSize, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 5, 20)
	if (err != nil) { return nil, err }

	oilLevels, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 1, 3)
	if (err != nil) { return nil, err }

	options := CTOpts{
		BlurKernelSize : blurKernelSize,
		EdgeThreshold : edgeThreshold,
		OilFilterSize : oilFilterSize,
		OilLevels : oilLevels,
	}
	
	cartoonEffectObject := NewCartoon(options)
	
	goeffectsImageObject, err := convertGolangImageObjectToGoeffectsImageObject(inputImage)
	if (err != nil) { return nil, err }	

	resultGoeffectsImage, err := cartoonEffectObject.Apply(&goeffectsImageObject, 8)
	if (err != nil) { return nil, err }
	
	result := convertGoeffectsImageObjectToGolangImageObject(*resultGoeffectsImage)
	
	return result, nil
}

func ApplyPencilEffect(inputImage image.Image, effectStrength int)(image.Image, error){
	
	if (inputImage == nil){
		return nil, errors.New("ApplyPencilEffect called with nil image.")
	}

	if (effectStrength < 0 || effectStrength > 100){
		return nil, errors.New("ApplyPencilEffect called with invalid effectStrength.")	
	}

	if (effectStrength == 0) {
		return inputImage, nil
	}

	goeffectsImageObject, err := convertGolangImageObjectToGoeffectsImageObject(inputImage)
	if (err != nil) { return nil, err }

	blurAmount, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 1, 20)
	if (err != nil) { return nil, err }

	if (blurAmount % 2 == 0) {
		blurAmount += 1	
	}

	pencilEffectObject := NewPencil(blurAmount)
	
	resultGoeffectsImage, err := pencilEffectObject.Apply(&goeffectsImageObject, 5)
	if (err != nil) { return nil, err }
	
	result := convertGoeffectsImageObjectToGolangImageObject(*resultGoeffectsImage)
	
	return result, nil
}

func ApplyWireframeEffect(inputImage image.Image, effectStrength int, lightMode bool)(image.Image, error){

	if (inputImage == nil){
		return nil, errors.New("ApplyWireframeEffect called with nil image.")
	}

	if (effectStrength < 0 || effectStrength > 100){
		return nil, errors.New("ApplyWireframeEffect called with invalid effectStrength.")	
	}

	if (effectStrength == 0) {
		return inputImage, nil
	}

	goeffectsImageObject, err := convertGolangImageObjectToGoeffectsImageObject(inputImage)
	if (err != nil) { return nil, err }

	const algorithm GSAlgo = 3
	
	grayscaleEffectObject := NewGrayscale(algorithm)

	grayscaleGoeffectsImage, err := grayscaleEffectObject.Apply(&goeffectsImageObject, 5)
	if (err != nil) { return nil, err }

	threshold, err := helpers.ScaleIntProportionally(false, effectStrength, 0, 100, 10, 100)
	if (err != nil) { return nil, err }
	
	sobelEffectObject := NewSobel(threshold, lightMode)
	
	resultGoeffectsImage, err := sobelEffectObject.Apply(grayscaleGoeffectsImage, 5)
	if (err != nil) { return nil, err }
	
	result := convertGoeffectsImageObjectToGolangImageObject(*resultGoeffectsImage)
	
	return result, nil
}

func ApplyOilPaintingEffect(inputImage image.Image, effectStrength int)(image.Image, error){
	
	if (inputImage == nil){
		return nil, errors.New("ApplyOilPaintingEffect called with nil image.")
	}

	if (effectStrength < 0 || effectStrength > 100){
		return nil, errors.New("ApplyOilPaintingEffect called with invalid effectStrength.")	
	}

	if (effectStrength == 0) {
		return inputImage, nil
	}

	filterSize, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 10, 30)
	if (err != nil) { return nil, err }

	levels, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 10, 70)
	if (err != nil) { return nil, err }

	oilPaintingEffectObject := NewOilPainting(filterSize, levels)
	
	goeffectsImageObject, err := convertGolangImageObjectToGoeffectsImageObject(inputImage)
	if (err != nil) { return nil, err }	

	resultGoeffectsImage, err := oilPaintingEffectObject.Apply(&goeffectsImageObject, 5)
	if (err != nil) { return nil, err }
	
	result := convertGoeffectsImageObjectToGolangImageObject(*resultGoeffectsImage)
	
	return result, nil
}

func convertGolangImageObjectToGoeffectsImageObject(inputImage image.Image)(Image, error){

	newImageRGBA := image.NewRGBA(inputImage.Bounds())
	draw.Draw(newImageRGBA, inputImage.Bounds(), inputImage, image.Point{}, draw.Over)
	
	width, height, err := imagery.GetImageWidthAndHeightPixels(inputImage)
	if (err != nil) {
		var failedImage Image
		return failedImage, err
	}

	goeffectsImage := Image{
		img:    newImageRGBA,
		Width:  width,
		Height: height,
		Bounds: Rect{X: 0, Y: 0, Width: width, Height: height},
	}

	return goeffectsImage, nil
}

func convertGoeffectsImageObjectToGolangImageObject(inputImage Image) image.Image {

	imageRGBA := inputImage.img

	imageWidth := inputImage.Width
	imageHeight := inputImage.Height

	newRectangle := image.Rect(0, 0, imageWidth, imageHeight)

	newImage := image.NewRGBA(newRectangle)
	
	draw.Draw(newImage, newImage.Bounds(), imageRGBA, image.Point{}, draw.Over)
	
	return newImage
}




