package ballistics.orbitalPoints.integrator

import ballistics.types.BallisticsError
import ballistics.types.EarthType
import ballistics.types.ModDVType

internal class IntegratorAdams(mdType: ModDVType, earthType: EarthType) : AbstractIntegrator(mdType, earthType) {
    private val rng: IntegratorRK4 = IntegratorRK4(mdType, earthType)

    val a2 =
        arrayOf(
            0.11367394179894179894E-1,
            -0.93840939153439153439E-1,
            0.343080357142857142857,
            -0.732035383597883597884,
            0.1017964616402116402116E+1,
            -0.1006919642857142857143E+1,
            0.1156159060846560846560E+1,
            0.304224537037037037037,
        )

    val a1 =
        arrayOf(
            -0.304224537037037037037,
            0.2445163690476190476190E+1,
            -0.8612127976190476190476E+1,
            0.17379654431216931216931E+2,
            -0.22027752976190476190476E+2,
            0.18054538690476190476190E+2,
            -0.9525206679894179894180E+1,
            0.3589955357142857142857E+1,
        )

    var intpoints =
        arrayOf(
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
            arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
        )

    override fun accelerate(x: Array<Double>): BallisticsError {
        for (m in 0..6)
            intpoints[0][m] = x[m]

        var er = prav.calculate(intpoints[0])
        if (er != BallisticsError.OK) {
            return er
        }
        for (n in 0..6)
            intpoints[9][n] = prav.y[n]

        // кратность шага по Рунге шагу по Адамсу
        val dStep = 2
        val ihrng = -step / dStep
        rng.step = ihrng
        // текущий элемент массива
        var j = 8

        val fld1 = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
        val fld2 = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
        val fld3 = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
        for (m in 0..6)
            fld1[m] = x[m]

        for (i in 0..6) {
            for (q in 0..1) {
                er = rng.nextStep(fld1)
                if (er != BallisticsError.OK) {
                    return er
                }
                for (m in 0..6)
                    fld2[m] = rng.y[m]

                for (m in 0..6)
                    fld1[m] = fld2[m]
            }

            er = prav.calculate(fld1)
            if (er != BallisticsError.OK) {
                return er
            }
            for (m in 0..6)
                fld3[m] = prav.y[m]

            for (m in 0..6)
                intpoints[j][m] = fld3[m]

            j--
        }

        return BallisticsError.OK
    }

    override fun setSBall(s: Double) {
        prav.sBall = s
        rng.setSBall(s)
    }

    private fun copyPoints(from : Int, to : Int){
        for (m in 0..6)
            intpoints[from][m] = intpoints[to][m]
    }

    override fun nextStep(x: Array<Double>): BallisticsError {
        for (m in 0..6)
            intpoints[1][m] = x[m]

        // Экстрaполяция
        var r: Double
        for (j in 1..6) {
            r = 0.0
            for (k in 1..8)
                r = r + intpoints[1 + k][j - 1] * a1[k - 1]

            intpoints[1][j - 1] = intpoints[1][j - 1] + r * step
        }
        intpoints[1][6] = intpoints[1][6] + step

        var er = prav.calculate(intpoints[1])
        if (er != BallisticsError.OK) {
            return er
        }
        for (n in 0..6)
            intpoints[10][n] = prav.y[n]

        copyPoints(1,0)

        // Интерполяция
        for (j in 1..6) {
            r = 0.0
            for (k in 1..8)
                r = r + intpoints[2 + k][j - 1] * a2[k - 1]

            intpoints[1][j - 1] = intpoints[1][j - 1] + step * r
        }
        intpoints[1][6] = intpoints[1][6] + step

        er = prav.calculate(intpoints[1])
        if (er != BallisticsError.OK)
            return er
        for (n in 0..6)
            intpoints[10][n] = prav.y[n]
        for (m in 0..6)
            y[m] = intpoints[1][m]
        for (j in 0..9)
            copyPoints(j, j+1)
        return BallisticsError.OK
    }
}
