package main

// RIGHT : x
// FORWARD : Y
// UP : Z

import (
	"math"
)

func phongShading(point Vector3, normal Vector3, mat Material) Vector3 {
	lightVec := (lightPos.Sub(point)).Normalized()
	reflectLight := Reflect(lightVec, normal)
	eyeVec := (cameraPos.Sub(point)).Normalized()
	brutMat := mat.GetMaterialBrut()
	ambiant := brutMat.ambiantColor.GetColor(point)

	diffusePower := brutMat.diffuseFac * max(0, normal.Dot(lightVec))
	diffuse := brutMat.diffuseColor.GetColor(point).Scale(diffusePower)

	specularPower := brutMat.specularFac * math.Pow(max(0, reflectLight.Dot(eyeVec)), brutMat.specularExp)
	specular := brutMat.specularColor.GetColor(point).Scale(specularPower)

	return ambiant.Add(diffuse).Add(specular)
}

func shadow(point Vector3) float64 {
	direction := (lightPos.Sub(point)).Normalized()
	p := point.Add(direction.Scale(5 * EPS))
	res := 1.0
	dist_total := 0.0
	for range MAX_STEP {
		dist := scene.Distance(p)
		if dist < EPS {
			return 0
		}
		if dist > MAX_DIST {
			break
		}
		res = min(res, SOFT_SHADOW_COEFF*dist/dist_total)
		dist_total += dist
		p.Radd(direction.Scale(dist))
	}
	return res
}

func reflect(point Vector3, direction Vector3, normal Vector3, mat Material, reflectNumber int) Vector3 {
	brutMat := mat.GetMaterialBrut()
	if reflectNumber <= 0 || brutMat.reflectanceFac < EPS {
		return Vector3{0, 0, 0}
	}
	reflected := Reflect(direction, normal).Scale(-1)
	reflectRayOrigin := point.Add(reflected.Scale(5 * EPS))
	hit, color := rayMarch(reflectRayOrigin, reflected, reflectNumber-1)
	if !hit {
		color = Vector3{0, 0, 0}
	}
	// fmt.Println("Reflect")
	return color.Scale(brutMat.reflectanceFac)
}

func rayMarch(origin Vector3, direction Vector3, reflectNumber int) (bool, Vector3) {
	p := origin
	for range MAX_STEP {
		dist := scene.Distance(p)
		if dist < EPS {
			mat := scene.GetMaterial(p)
			normal := Gradient(scene, p, EPS).Normalized()
			phongShade := phongShading(p, normal, mat)
			shadowCoeff := max(shadow(p), 0.3)
			reflectColor := reflect(p, direction, normal, mat, reflectNumber)
			return true, phongShade.Add(reflectColor).Scale(shadowCoeff)
			// return true, phongShading(p, normal, *mat)
		}
		if dist > MAX_DIST {
			break
		}
		p.Radd(direction.Scale(dist))
	}
	return false, GREY
}
