package ballistics.mpl

import ballistics.flightLine.FlightLineCalculator
import ballistics.types.BallisticsError
import ballistics.types.OPKatObj
import ballistics.types.PointViewParams
import ballistics.types.TangageType
import ballistics.utils.math.Vector3D
import ballistics.utils.math.equations.EquationCalculatorSpan
import kotlin.math.PI
import kotlin.math.abs

internal class MPLCalculator(val flightLineCalculator: FlightLineCalculator, val tangage: Double, tangageType: TangageType) {
    private val debugEasyCalc = false
    private val stepper = flightLineCalculator.opc.getStepper()
    private val orientCalculator = OrientOnPointCalculator(flightLineCalculator.opc.earthType, flightLineCalculator.wcs, tangageType)
    private var currentObj = Vector3D()
    var sunAngleMin: Double = 0.0
    var useObjConstraints: Boolean = false

    var krenMin = 0.0
    var krenMax = 0.0
    var krenByModule : Boolean = true

    var mpl = mutableListOf<PointViewParams>()

    fun calculate(
        tn: Double,
        tk: Double,
        objs: Iterable<OPKatObj>,
    ): BallisticsError {
        mpl.clear()
        val eqc = EquationCalculatorSpan()
        eqc.delta = 0.001 * PI / 180.0
        eqc.value = tangage

        val samin = sunAngleMin
//        krenMin = flightLineCalculator.rollMin
//        krenMax = flightLineCalculator.rollMax


        for (o in objs) {
            if (useObjConstraints) {
                krenMin = o.rollMin
                krenMax = o.rollMax
                sunAngleMin = o.sunAngleMin
            }

            currentObj = flightLineCalculator.eart.blh2xyz(o.lat, o.long, o.height)

            val covs =
                flightLineCalculator.earthCoveringCalculator.getCoverings(
                    o.lat,
                    o.long,
                ).filter { (it.tStop >= tn && it.tStart <= tk) }
            var tLast = 0.0
            for (cov in covs) {
                if (cov.tStart - tLast < 600.0) {
                    continue
                }

                var isOk: Boolean
                var traverz: Double?
                if (!debugEasyCalc) {
                    traverz = eqc.calculate(cov.tStart, cov.tStop, this::equation)
                    isOk = traverz != null
                } else {
                    traverz = (cov.tStart + cov.tStop) / 2
                    isOk = true
                }

                if (isOk &&  tryToAddViewParams(traverz!!, o)) {
                        tLast = traverz
                }
            }
        }
        sunAngleMin = samin
        mpl.sortBy { it.traverz }
        return BallisticsError.OK
    }

    fun tryToAddViewParams(
        t: Double,
        o: OPKatObj,
    ): Boolean {
        val ka = stepper.calculate(t)
        if (ka != null) {
            val orient = orientCalculator.calculateOrientOnPoint(ka, currentObj)
            val w = orientCalculator.pointInWCS(ka, currentObj)
            val blh = flightLineCalculator.eart.xyz2blh(ka.r)

            val vp =
                PointViewParams(
                    o.objON,
                    o.objN,
                    o.objUUID,
                    o.pointNumb,
                    ka.vit,
                    t,
                    blh.lat,
                    blh.long,
                    orient,
                    w.module(),
                    orientCalculator.astro.sunAngle(t, currentObj),
                    orientCalculator.calculateVisirAngle(ka, o.lat, o.long, o.height),
                    if (ka.v.z > 0) 0 else 1
                    )

            if ((vp.sunAngle >= sunAngleMin) && ( ( krenByModule &&
                (abs(vp.orientation.kren) >= krenMin) &&
                (abs(vp.orientation.kren) <= krenMax)
                        ) ||
                        ( !krenByModule &&
                                (vp.orientation.kren >= krenMin) &&
                                (vp.orientation.kren <= krenMax)
                                )
                        )
            ) {
                mpl.add(vp)
            }
            return true
        }

        return false
    }

    private fun equation(x: Double): Double {
        var tang = -1.0
        val ka = stepper.calculate(x)
        if (ka != null) {
            val orient = orientCalculator.calculateOrientOnPoint(ka, currentObj)
            tang = orient.tang
        }
        return tang
    }

    fun clear(){
        flightLineCalculator.clear()
        stepper.clear()
        mpl.clear()

    }
}
