package basic

import (
	"math"

	"b612.me/astro/planet"
	. "b612.me/astro/tools"
)

// SaturnRingParameters 土星环参数 / Saturn ring parameters.
func SaturnRingParameters(jd float64) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) {
	return SaturnRingParametersN(jd, -1)
}

// SaturnRingParametersN 土星环参数（截断版） / truncated Saturn ring parameters.
func SaturnRingParametersN(jd float64, n int) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) {
	inclination, node := saturnRingPlane(jd)
	earthLon, earthLat := SaturnApparentLoBoN(jd, n)
	sunLon, sunLat := saturnRingSunLoBo(jd, n, node)

	earthLatitude = saturnRingLatitude(inclination, node, earthLon, earthLat)
	sunLatitude = saturnRingLatitude(inclination, node, sunLon, sunLat)
	positionAngle = saturnRingPositionAngle(jd, inclination, node, earthLon, earthLat)
	earthU := saturnRingLongitude(inclination, node, earthLon, earthLat)
	sunU := saturnRingLongitude(inclination, node, sunLon, sunLat)
	deltaU = saturnRingLongitudeDelta(earthU, sunU)

	earthDistance := EarthSaturnAwayN(jd, n)
	majorAxis = 375.35 / earthDistance
	minorAxis = majorAxis * math.Abs(Sin(earthLatitude))
	return
}

// SaturnRingB 土星环张角 B / Saturn ring opening angle B.
func SaturnRingB(jd float64) float64 {
	return SaturnRingBN(jd, -1)
}

// SaturnRingBN 土星环张角 B（截断版） / truncated Saturn ring opening angle B.
func SaturnRingBN(jd float64, n int) float64 {
	earthLatitude, _, _, _, _, _ := SaturnRingParametersN(jd, n)
	return earthLatitude
}

// SaturnRingSunB 土星环太阳侧张角 B' / Sun-side Saturn ring opening angle B'.
func SaturnRingSunB(jd float64) float64 {
	return SaturnRingSunBN(jd, -1)
}

// SaturnRingSunBN 土星环太阳侧张角 B'（截断版） / truncated Sun-side Saturn ring opening angle B'.
func SaturnRingSunBN(jd float64, n int) float64 {
	_, sunLatitude, _, _, _, _ := SaturnRingParametersN(jd, n)
	return sunLatitude
}

// SaturnRingPositionAngle 土星环北半短轴位置角 / position angle of Saturn ring northern semiminor axis.
func SaturnRingPositionAngle(jd float64) float64 {
	return SaturnRingPositionAngleN(jd, -1)
}

// SaturnRingPositionAngleN 土星环北半短轴位置角（截断版） / truncated position angle of Saturn ring northern semiminor axis.
func SaturnRingPositionAngleN(jd float64, n int) float64 {
	_, _, positionAngle, _, _, _ := SaturnRingParametersN(jd, n)
	return positionAngle
}

// SaturnRingDeltaU 太阳和地球在环面内的土星心黄经差 / difference of Saturnicentric ring longitudes.
func SaturnRingDeltaU(jd float64) float64 {
	return SaturnRingDeltaUN(jd, -1)
}

// SaturnRingDeltaUN 太阳和地球在环面内的土星心黄经差（截断版） / truncated Saturnicentric ring longitude difference.
func SaturnRingDeltaUN(jd float64, n int) float64 {
	_, _, _, deltaU, _, _ := SaturnRingParametersN(jd, n)
	return deltaU
}

// SaturnRingAxis 土星环外缘长短轴，单位角秒 / outer ring axes in arcseconds.
func SaturnRingAxis(jd float64) (majorAxis, minorAxis float64) {
	return SaturnRingAxisN(jd, -1)
}

// SaturnRingAxisN 土星环外缘长短轴（截断版），单位角秒 / truncated outer ring axes in arcseconds.
func SaturnRingAxisN(jd float64, n int) (majorAxis, minorAxis float64) {
	_, _, _, _, majorAxis, minorAxis = SaturnRingParametersN(jd, n)
	return
}

func saturnRingPlane(jd float64) (inclination, node float64) {
	t := (jd - 2451545.0) / 36525.0
	inclination = 28.075216 - 0.012998*t + 0.000004*t*t
	node = 169.508470 + 1.394681*t + 0.000412*t*t
	return
}

func saturnRingSunLoBo(jd float64, n int, node float64) (lon, lat float64) {
	lon = planet.WherePlanetN(5, 0, jd, n)
	lat = planet.WherePlanetN(5, 1, jd, n)
	distance := planet.WherePlanetN(5, 2, jd, n)
	lat -= 0.000764 * Cos(lon-node) / distance
	lon -= 0.01759 / distance
	return lon, lat
}

func saturnRingLatitude(inclination, node, lon, lat float64) float64 {
	return ArcSin(Sin(inclination)*Cos(lat)*Sin(lon-node) - Cos(inclination)*Sin(lat))
}

func saturnRingLongitude(inclination, node, lon, lat float64) float64 {
	y := Sin(inclination)*Sin(lat) + Cos(inclination)*Cos(lat)*Sin(lon-node)
	x := Cos(lat) * Cos(lon-node)
	return ArcTan2(y, x)
}

func saturnRingLongitudeDelta(a, b float64) float64 {
	delta := math.Abs(Limit360(a - b))
	if delta > 180 {
		return 360 - delta
	}
	return delta
}

func saturnRingPositionAngle(jd, inclination, node, lon, lat float64) float64 {
	poleLon := node - 90 + Nutation2000Bi(jd)
	poleLat := 90 - inclination
	eps := TrueObliquity(jd)
	poleRa, poleDec := saturnRingEclipticToEquatorial(poleLon, poleLat, eps)
	saturnRa, saturnDec := saturnRingEclipticToEquatorial(lon, lat, eps)

	y := Cos(poleDec) * Sin(poleRa-saturnRa)
	x := Sin(poleDec)*Cos(saturnDec) - Cos(poleDec)*Sin(saturnDec)*Cos(poleRa-saturnRa)
	return ArcTan2(y, x)
}

func saturnRingEclipticToEquatorial(lon, lat, eps float64) (ra, dec float64) {
	ra = ArcTan2(Sin(lon)*Cos(eps)-Tan(lat)*Sin(eps), Cos(lon))
	dec = ArcSin(Sin(lat)*Cos(eps) + Cos(lat)*Sin(eps)*Sin(lon))
	return
}
