package producer

import (
	"gocv.io/x/gocv"
)

const (
	NoEnhancement                = "NoEnhancement"
	StretchToMinimumMaximum      = "StretchToMinimumMaximum"
	StretchToCumulativeCutMinMax = "StretchToCumulativeCutMinMax"
)

type ContrastEnhancement struct {
	minimumValue         int
	maximumValue         int
	lookupTableOffset    int
	lookupTable          []int
	mRasterDataTypeRange int
	enhancementType      string
	//!< Range is [ min + cumulativeCutLower() * (max - min), min + cumulativeCutUpper() * (max - min) ]
	cumulativeCutLower float64
	cumulativeCutUpper float64
}

func NewContrastEnhancement(minimumValue, maximumValue int) *ContrastEnhancement {
	ce := &ContrastEnhancement{
		enhancementType:    StretchToMinimumMaximum,
		cumulativeCutLower: 0,
		cumulativeCutUpper: 1,
	}
	ce.minimumValue = minimumValue + int(ce.cumulativeCutLower)*(maximumValue-minimumValue)
	ce.maximumValue = minimumValue + int(ce.cumulativeCutUpper)*(maximumValue-minimumValue)
	ce.mRasterDataTypeRange = ce.maximumValue - ce.minimumValue
	ce.lookupTableOffset = -1 * ce.minimumValue
	ce.lookupTable = make([]int, ce.mRasterDataTypeRange+1)
	ce.generateLookupTable()

	return ce
}

func (ce ContrastEnhancement) enhance(value int) int {
	if ce.enhancementType != NoEnhancement {
		shiftedValue := value + ce.lookupTableOffset
		if shiftedValue >= 0 && shiftedValue < ce.mRasterDataTypeRange+1 {
			return ce.lookupTable[shiftedValue]
		}
		return 0
	}

	return ce.linearMinMaxEnhancement(value)
}

func (ce *ContrastEnhancement) generateLookupTable() {
	for i := range ce.lookupTable {
		ce.lookupTable[i] = ce.linearMinMaxEnhancement(i - ce.lookupTableOffset)
	}
}

func (ce ContrastEnhancement) linearMinMaxEnhancement(value int) int {
	stretchedValue := (float64(value-ce.minimumValue) / float64(ce.mRasterDataTypeRange)) * 255.0
	if stretchedValue < 0 {
		return 0
	} else if stretchedValue > 255 {
		return 255
	}
	return int(stretchedValue)
}

func cumulativeCountCutEnhancement(img gocv.Mat, cumulativeCutLower, cumulativeCutUpper float64) gocv.Mat {
	dataTypeMax := 65536
	// 计算直方图
	hist := make([]int, dataTypeMax)
	for y := 0; y < img.Rows(); y++ {
		for x := 0; x < img.Cols(); x++ {
			hist[uint16(img.GetShortAt(y, x))]++
		}
	}

	// 计算累积直方图
	cdf := make([]int, dataTypeMax)
	cdf[0] = hist[0]
	for i := 1; i < dataTypeMax; i++ {
		cdf[i] = cdf[i-1] + hist[i]
	}

	// 计算低高阈值
	totalPixels := img.Rows() * img.Cols()
	lowCutoff := int(float64(totalPixels) * cumulativeCutLower)
	highCutoff := int(float64(totalPixels) * cumulativeCutUpper)

	var lowerBound, upperBound int
	for i, v := range cdf {
		if v > lowCutoff {
			lowerBound = i
			break
		}
	}
	for i := dataTypeMax - 1; i >= 0; i-- {
		if cdf[i] < highCutoff {
			upperBound = i
			break
		}
	}

	// 重新映射灰度值
	enhancedImg := gocv.NewMatWithSize(img.Rows(), img.Cols(), gocv.MatTypeCV8U)
	for y := 0; y < img.Rows(); y++ {
		for x := 0; x < img.Cols(); x++ {
			oldVal := uint16(img.GetShortAt(y, x))
			newVal := (int(oldVal) - lowerBound) * 255 / (upperBound - lowerBound)
			if newVal > 255 {
				newVal = 255
			}
			if newVal < 0 {
				newVal = 0
			}

			enhancedImg.SetUCharAt(y, x, uint8(newVal))
		}
	}

	return enhancedImg

}
