package rrc

import (
	"math"

	log "github.com/sirupsen/logrus"
	"gocv.io/x/gocv"
)

// Destriping multisensor imagery with moment matching [Gadallah, 2000]
func DoMomentMatching(originalImg gocv.Mat) {
	probes := originalImg.Cols()
	log.Printf("do moment matching for %d probes, %d rows", probes, originalImg.Rows())
	// 第i个探元的像元均值和标准差
	means := make([]float64, probes)
	stds := make([]float64, probes)

	// 计算每个探元的均值和标准差
	for x := 0; x < originalImg.Cols(); x++ {
		var total int64
		n := originalImg.Rows()
		var dn uint16
		for y := 0; y < n; y++ {
			dn = uint16(originalImg.GetShortAt(y, x))
			total += int64(dn)
		}
		means[x] = float64(total) / float64(n)

		var a float64
		for y := 0; y < n; y++ {
			dn = uint16(originalImg.GetShortAt(y, x))
			a += math.Pow(float64(dn)-means[x], 2)
		}
		stds[x] = math.Sqrt(a / float64(n))
	}

	// 列参考值和列参考标准差
	var mu, sig float64
	for x := 0; x < probes; x++ {
		mu += means[x]
		sig += stds[x]
	}

	mu = mu / float64(probes)
	sig = sig / float64(probes)

	log.Printf("mean reference value: %f, std reference value: %f", mu, sig)

	// 修正 DN_adjusted[i] = (DN[i] - means[i]) *sig/stds[i]+mu
	for x := 0; x < originalImg.Cols(); x++ {
		for y := 0; y < originalImg.Rows(); y++ {
			dn := uint16(originalImg.GetShortAt(y, x))
			dn_adjusted := uint16((float64(dn)-means[x])*sig/stds[x] + mu)
			originalImg.SetShortAt(y, x, int16(dn_adjusted))
		}
	}
}

// 相邻列均衡算法（张兵，2006）- 实测结果不如上面的矩匹配算法
func DoMomentMatching2006(originalImg gocv.Mat) {
	probes := originalImg.Cols()
	log.Printf("do moment matching for %d probes", probes)
	// 第i个探元的像元均值和标准差
	means := make([]float64, probes)
	stds := make([]float64, probes)
	// 计算每个探元的均值和标准差
	for x := 0; x < originalImg.Cols(); x++ {
		var total int64
		n := originalImg.Rows()
		var dn uint16
		for y := 0; y < n; y++ {
			dn = uint16(originalImg.GetShortAt(y, x))
			total += int64(dn)
		}
		means[x] = float64(total) / float64(n)

		var a float64
		for y := 0; y < n; y++ {
			dn = uint16(originalImg.GetShortAt(y, x))
			a += math.Pow(float64(dn)-means[x], 2)
		}
		stds[x] = math.Sqrt(a / float64(n))
	}

	// 修正 DN_adjusted[i] = (DN[i] - means[i]) *sig/stds[i]+mu
	for x := 1; x < originalImg.Cols()-1; x++ {
		// 列参考值和列参考标准差
		mu := (means[x-1] + means[x+1]/2 + means[x]) / 2
		sig := (stds[x-1] + stds[x+1]/2 + stds[x]) / 2
		for y := 0; y < originalImg.Rows(); y++ {
			dn := uint16(originalImg.GetShortAt(y, x))
			dn_adjusted := uint16((float64(dn)-means[x])*sig/stds[x] + mu)
			originalImg.SetShortAt(y, x, int16(dn_adjusted))
		}
	}
}
