package ballistics.types

import ballistics.utils.math.Vector3D
import java.time.LocalDateTime
import kotlin.math.PI

/**
 * Тип метода интегрирования
 */
enum class IntegrationType {
    RUNG4,
    ADAMS7,
}

/**
 * Тип модели движения
 */
enum class ModDVType(val value: Int) {
    FOTO(1),

    KONDOR(6),

    METEORM1(14),
    METEORM2(15),

    BARS(16),

    KONDOR_PROGNOZ(19),
    ;

    companion object {
        val map = values().associate { it.value to it }

        fun fromInt(
            value: Int,
            defValue: ModDVType = FOTO,
        ): ModDVType {
            return map[value] ?: defValue
        }
    }
}

/**
 * Тип модели Земли
 */
enum class EarthType {
    PZ90d0,
    PZ90d02,
}

enum class AstroType {
    ATJ2000,
}

/**
 * Тип базовой СК
 */
enum class WorkCSType {
    /**
     * орбитальная
     */
    WCSOrbit,

    /**
     * путевая / трассовая
     */
    WCSPath,
}

/**
 * Тип учета угла тангажа в расчете матрицы планирования
 */
enum class TangageType {
    /**
     * Конструктивный
     */
    TTConstructive,

    /**
     * Упреждающий
     */
    TTProactive,
}


/**
 * Параметры точки орбиты
 */
data class OrbitalPoint(val t: Double, var vit: Int, val r: Vector3D, val v: Vector3D) {
    constructor() : this(0.0, 0, Vector3D(), Vector3D())
}

/**
 * Параметры витка (ВУЗ)
 */
data class RevolutionParameter(var vuz: OrbitalPoint, var lVuz: Double, var hVuz: Double) {
    constructor() : this(OrbitalPoint(), 0.0, 0.0)
}

/**
 * Начальные условия движения
 */
class InitialConditions(var point: OrbitalPoint, var sBall: Double, var f81: Double) {
    constructor() : this(OrbitalPoint(), 0.0, 100.0)
}

class TLE(val tle1: String, val tle2: String) {
    constructor() : this("", "")
}

/**
 * затенение по азимуму
 * азимут в диапазоне 0..2*PI
 * при переходе через 0 долготы нужно заводить 2 записи! до 0 и после 0!
 */
data class ShadowAU(
    val azimuthStart: Double,
    val azimuthEnd: Double,
    val elevation: Double,
)

/**
 * Параметры ППИ
 */
data class PPI(
    val ppiNum: Int = 0,
    val auNum: Int = 0,
    val lat: Double = 0.0,
    val long: Double = 0.0,
    val height: Double = 0.0,
    val elevMin: Double = 0.0,
    val elevMax: Double = 0.0,
    var shadowMin: List<ShadowAU>? = null,
    var shadowMax: List<ShadowAU>? = null,
) {
    constructor(
        ppiNum: Int,
        auNum: Int,
        lat: Double,
        long: Double,
        height: Double,
        elevMin: Double,
        elevMax: Double,
    ) : this (ppiNum, auNum, lat, long, height, elevMin, elevMax, null, null)
}

/**
 * Параметры ППИ(расширенные)
 */
internal class PPIExtParams() {
    var nip = 0
    var au = 0
    var bn = 0.0
    var xn = 0.0
    var yn = 0.0
    var zn = 0.0
    var sl = 0.0
    var cl = 0.0
    var sb = 0.0
    var cb = 0.0
}

/**
 * Параметры ППИ(расширенные)
 */
internal class PPIExt() {
    var params = PPIExtParams()
    var elevMin = 0.0
    var elevMax = 0.0
    var zoneIn = VisibilityParametersZRV()
    var zoneMax = VisibilityParametersZRV()
    var zoneOut = VisibilityParametersZRV()
    var vit = 0
    var kaY = 0.0
    var t1 = 0.0
    var gam1 = 0.0
    var a1 = 0.0
    var vy1 = 0.0
    var t2 = 0.0
    var gam2 = 0.0
    var a2 = 0.0
    var vy2 = 0.0
    var shadowMin: Iterable<ShadowAU>? = null
    var shadowMax: Iterable<ShadowAU>? = null

    fun isVisible(
        azimuth: Double,
        elevation: Double,
    ): Boolean {
        if (elevation > elevMax) {
            return false
        }
        if (shadowMin != null) {
            val a = if (azimuth >= 0) azimuth else azimuth + 2 * PI
            val shad = shadowMin!!.find { it.azimuthStart <= a && a <= it.azimuthEnd }
            if (shad != null && shad.elevation > elevation) {
                return false
            }
        }
        if (shadowMax != null) {
            val a = if (azimuth >= 0) azimuth else azimuth + 2 * PI
            val shad = shadowMax!!.find { it.azimuthStart <= a && a <= it.azimuthEnd }
            if (shad != null && shad.elevation < elevation) {
                return false
            }
        }
        return true
    }
}

/**
 * Параметры видимости КА-ППИ
 */
data class VisibilityParametersZRV(var t: Double, var range: Double, var azimuth: Double, var elevation: Double) {
    constructor() : this(0.0, 0.0, 0.0, 0.0)
}

/**
 * Параметры зоны радиовидимости КА-ППИ
 */
data class ZRV(
    val ppi: Int,
    val au: Int,
    val vit: Int,
    val zoneIn: VisibilityParametersZRV,
    val zoneMax: VisibilityParametersZRV,
    val zoneOut: VisibilityParametersZRV,
)

class Orientation(
    var tang: Double,
    var kren: Double,
    var risk: Double,
)

/**
 * Параметры точки на Земной поверхности
 */
class BLHPoint(
    var lat: Double,
    var long: Double,
    var h: Double,
)

/**
 * Параметры точки на границе полосы обзора / трассе полета
 */
class THBLPoint(val lat: Double, val long: Double, val range: Double, val sunAngle: Double)

/**
 * Параметры трассы полета и полосы обзора
 */
class FlightLine(
    val t: Double,
    val vit: Int,
    val pv: Int,
    val rightOuterSwath: THBLPoint,
    val rightInternalSwath: THBLPoint,
    val flightLine: THBLPoint,
    val leftInternalSwath: THBLPoint,
    val leftOuterSwath: THBLPoint,
)

class FleghtLineSector(
    val tStart: Double,
    val tStop: Double,
)

/**
 * Объект оперативного каталога
 */
class OPKatObj(
    val objON: Int,
    val objN: Int,
    val objUUID : String,
    val pointNumb: Int,
    val lat: Double,
    val long: Double,
    val height: Double,
    val rollMin: Double,
    val rollMax: Double,
    val sunAngleMin: Double,
)

/**
 * Параметры видимости точки наблюдения
 */
class PointViewParams(
    val objON: Int,
    val objN: Int,
    val objUUID : String,
    val pointNumb: Int,
    val vit: Int,
    val traverz: Double,
    val latTraverz: Double,
    val longTraverz: Double,
    val orientation: Orientation,
    val range: Double,
    val sunAngle: Double,
    val sightAngle: Double,
    val pv : Int,
)

data class KeplerParams(
    var dmv: Double = 0.0,
    var ael: Double = 0.0,
    var e: Double = 0.0,
    var nakl: Double = 0.0,
    var omegab: Double = 0.0,
    var omegam: Double = 0.0,
    var u: Double = 0.0,
    var v: Double = 0.0,
    var o: Double = 0.0,
    var eA: Double = 0.0,
    var tau: Double = 0.0,
    var t: Double = 0.0,
    var rP: Double = 0.0,
    var rA: Double = 0.0,
)


class TLEParams(
    var satName : String = "",
    var noradId : Long = 0,
    var revolution : Long = 0,
    var time : LocalDateTime = LocalDateTime.now(),
    var inclination : Double = 0.0,
    var perigee : Double = 0.0,
    var apogee: Double = 0.0,
    var argPerigee: Double = 0.0,
    var eccentricity: Double = 0.0,
    var major: Double = 0.0,
    var minor: Double = 0.0,
    var meanAnomaly: Double = 0.0,
    var period: Double = 0.0,
    var semiMajor: Double = 0.0,
    var semiMinor: Double = 0.0,
    var ascendingNode: Double = 0.0,
    var meanMotion: Double = 0.0,
    var meanMotionTLE: Double = 0.0
)