/*
 * Copyright (C) 2022 - Florindo Smilari (spherIC)
 */
package ch.spherIC.recurvebowsight.logic;

import android.os.AsyncTask;


/**
 * @author FC Smilari
 */
public class CalculationTask extends AsyncTask<TrajectoryCalculator, Integer, TrajectoryCalculator> {

    private static final Double PHI = 1.0;
    TrajectoryCalculator calculator;

    @Override
    protected TrajectoryCalculator doInBackground(final TrajectoryCalculator... params) {

        this.calculator = params[0];

        boolean exitLoop = false;
        Double angleX = 0.0;
        Double angleHigh = 0.0;
        Double angleLow = this.calculator.getStartAngle() + PHI;
        Double[][] results;
        Double frictionCoefficient = this.calculator.calculateFrictionCoeficient();

        // Startwerte fuer angleLow und angleHigh berechnen
        do {
            angleLow -= PHI;
            results = this.calculator.calculateTrajectory(angleLow, frictionCoefficient);
        } while (results[results.length - 1][TrajectoryCalculator.IDX_Y] - this.calculator.getDeltaHeight() > 0);

        angleHigh = angleLow;

        do {
            angleHigh += PHI;
            results = this.calculator.calculateTrajectory(angleHigh, frictionCoefficient);
        } while (results[results.length - 1][TrajectoryCalculator.IDX_Y] - this.calculator.getDeltaHeight() < 0);


        // Winkel suchen mit Hilfe der Regula Falsi und Flugbahnberechnungsiterationen
        do {
            angleX = (angleHigh + angleLow) / 2;
            results = this.calculator.calculateTrajectory(angleX, frictionCoefficient);

            if (Math.abs(this.calculator.getDeltaHeight() - results[results.length - 1][TrajectoryCalculator.IDX_Y])
                    > this.calculator.getCalculationPrecision()) {

                if (results[results.length - 1][TrajectoryCalculator.IDX_Y] - this.calculator.getDeltaHeight() < 0) {
                    angleLow = angleX;
                } else {
                    angleHigh = angleX;
                }

            } else {
                exitLoop = true;
            }

        } while (!exitLoop);

        this.calculator.setStartAngle(angleX);
        this.calculator.setFlightCurve(results);
        this.calculator.setMaxHCurve(calculateMaxHeight(results));

        publishProgress(0, results.length - 1);
        sleep(5);

        for (int i = 0; i < results.length; i++) {
            publishProgress(1, i);
            sleep(5);
        }

        return this.calculator;
    }

    public static Double[] calculateMaxHeight(final Double[][] flightCurve) {
        Double[] maxHCurve = new Double[]{0d, 0d};
        Double maxH = 0.0;

        for (int i = 0; i < flightCurve.length; i++) {
            if (flightCurve[i][TrajectoryCalculator.IDX_Y] > maxH) {
                maxH = flightCurve[i][TrajectoryCalculator.IDX_Y];
                maxHCurve[0] = maxH;
                maxHCurve[1] = flightCurve[i][TrajectoryCalculator.IDX_T];
            }
        }

        return maxHCurve;
    }

    @Override
    protected void onProgressUpdate(final Integer... values) {
        super.onProgressUpdate(values);
        this.calculator.getListener().updateTrajectoryTxtFld(values[0], this.calculator.getFlightCurve()[values[1]], this.calculator.getStartAngle(),
                                                             this.calculator.getMaxHCurve());
    }

    @Override
    protected void onPostExecute(final TrajectoryCalculator calculator) {
        super.onPostExecute(calculator);
        Double[][] flightCurve = calculator.getFlightCurve();
        Double flightDuration = flightCurve[flightCurve.length - 1][0];
        BallisticResults ballisticResults = new BallisticResults(flightCurve, calculator.getStartAngle(), calculator.getMaxHCurve(), flightDuration);
        calculator.getListener().onCalculationDone(ballisticResults);
    }

    private void sleep(final long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
