package org.nstart.dep265.tletools.zeptomoby.core

import org.nstart.dep265.tletools.zeptomoby.core.Globals.deg2rad
import org.nstart.dep265.tletools.zeptomoby.core.Globals.rad2deg
import org.nstart.dep265.tletools.zeptomoby.core.Globals.sqr
import org.nstart.dep265.tletools.zeptomoby.core.coord.Geo
import org.nstart.dep265.tletools.zeptomoby.core.coord.Topo
import org.nstart.dep265.tletools.zeptomoby.core.eci.EciTime
import kotlin.math.*

@Suppress("unused")
open class Site {

    constructor(degLat: Double, degLon: Double, kmAlt: Double, n: String) {
        name = n
        geo = Geo(deg2rad(degLat), deg2rad(degLon), kmAlt)
    }

    constructor(degLat: Double, degLon: Double, kmAlt: Double):
            this(degLat, degLon, kmAlt, "")

    constructor(g: Geo) {
        name = ""
        geo = g
    }

    override fun toString() = if (name.isEmpty()) "$geo" else "$name $geo"

    fun positionEci(julian: org.nstart.dep265.tletools.zeptomoby.core.Julian): EciTime = EciTime(geo, julian)

    @Deprecated("", ReplaceWith("positionEci(geo, julian)"))
    fun position(julian: org.nstart.dep265.tletools.zeptomoby.core.Julian): EciTime = EciTime(geo, julian)

    fun lookAngle(time: EciTime): Topo {
        val date: org.nstart.dep265.tletools.zeptomoby.core.Julian = time.date
        val eciSite = EciTime(geo, date)

        val vecRgRate = org.nstart.dep265.tletools.zeptomoby.core.Vector(
            time.velocity.x - eciSite.velocity.x,
            time.velocity.y - eciSite.velocity.y,
            time.velocity.z - eciSite.velocity.z
        )

        val x: Double = time.position.x - eciSite.position.x
        val y: Double = time.position.y - eciSite.position.y
        val z: Double = time.position.z - eciSite.position.z
        val w: Double = sqrt(sqr(x) + sqr(y) + sqr(z))
        val vecRange = org.nstart.dep265.tletools.zeptomoby.core.Vector(x, y, z, w)

        val theta = date.toLMST(longitudeRad)
        val sinLat = sin(latitudeRad)
        val cosLat = cos(latitudeRad)
        val sinTheta = sin(theta)
        val cosTheta = cos(theta)

        val topS = sinLat * (cosTheta * vecRange.x + sinTheta * vecRange.y) - cosLat * vecRange.z
        val topE = -sinTheta * vecRange.x + cosTheta * vecRange.y
        val topZ = cosLat * (cosTheta * vecRange.x + sinTheta * vecRange.y) + sinLat * vecRange.z
        var az = atan(-topE / topS)

        if (topS > 0.0) az += org.nstart.dep265.tletools.zeptomoby.core.Globals.pi
        if (az < 0.0) az += org.nstart.dep265.tletools.zeptomoby.core.Globals.twoPi

        var el = asin(topZ / vecRange.w)
        val rate = (vecRange.x * vecRgRate.x +
                vecRange.y * vecRgRate.y +
                vecRange.z * vecRgRate.z) / vecRange.w

        if (atmosphericCorrection) {
            val saveEl = el

            el += deg2rad(
                (1.02 / tan(
                    deg2rad(rad2deg(el) + 10.3 / (rad2deg(el) + 5.11))
                )) / 60.0
            )

            if (el < 0.0) el = saveEl
            if (el > org.nstart.dep265.tletools.zeptomoby.core.Globals.pi * 0.5) el = org.nstart.dep265.tletools.zeptomoby.core.Globals.pi * 0.5
        }

        return Topo(az,           // azimuth,   radians
                    el,           // elevation, radians
                    vecRange.w,   // range, km
                    rate          // rate,  km / sec
        )
    }

    val latitudeRad: Double
        get() = geo.latitudeRad
    val longitudeRad: Double
        get() = geo.longitudeRad
    val latitudeDeg: Double
        get() = geo.latitudeDeg
    val longitudeDeg: Double
        get() = geo.longitudeDeg

    val name: String
    val geo: Geo
    val atmosphericCorrection = false
}