package source;

/**
 *
 * @author Radoslaf
 */
public class Calculation {

    private Navigation navigation = null;
    double lat1 = 0.0;
    double lon1 = 0.0;
    double lat2 = 0.0;
    double lon2 = 0.0;

    public Calculation(Navigation navigation) {
        this.navigation = navigation;
    }

    private double retype(short[] in) {
        double a = in[0];
        double b = in[1];
        double c = in[2];
        double d = in[3];
        double e = a + b / 60 + c / 3600 + d / 360000;
        return e;
    }

    private void datatransform() {
        // Prevod dat z meho formatu na desetinne cislo

        lat1 = retype(navigation.navi.handler.getLatitude());
        lon1 = retype(navigation.navi.handler.getLongitude());
        lat2 = retype(navigation.navi.findlatitude);
        lon2 = retype(navigation.navi.findlongitude);

        // Určení polokoule ve sférických souřadnicích
        if (navigation.navi.handler.getWE().equals("W")) {
            lat1 = 180 - lat1;
        }
        if (navigation.navi.handler.getNS().equals("S")) {
            lon1 = 180 - lon1;
        }
        if (navigation.navi.fWE.equals("W")) {
            lat2 = 180 - lat2;
        }
        if (navigation.navi.fNS.equals("S")) {
            lon2 = 180 - lon2;
        }
    }

    public double distance() {
        /*
        The Haversine formula according to Dr. Math.
        http://mathforum.org/library/drmath/view/51879.html
        
        dlon = lon2 - lon1
        dlat = lat2 - lat1
        a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
        c = 2 * atan2(sqrt(a), sqrt(1-a))
        d = R * c
        
        Where
         * dlon is the change in longitude
         * dlat is the change in latitude
         * c is the great circle distance in Radians.
         * R is the radius of a spherical Earth.
         * The locations of the two points in
        spherical coordinates (longitude and
        latitude) are lon1,lat1 and lon2, lat2.
         */

        datatransform();
        double distance = 0.0;

        double diflat = Math.toRadians(lat2 - lat1);
        double diflon = Math.toRadians(lon2 - lon1);

        double a = Math.sin(diflat / 2) * Math.sin(diflat / 2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(diflon / 2) * Math.sin(diflon / 2);
        double c = 2 * this.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // Jednotky jsou v metrech
        int earthdiametre = 6378137;
        distance = earthdiametre * c;
        return distance;
    }

    public short angle() {

        datatransform();
        short angle = 0;

        double diflat = Math.toRadians(lat2 - lat1);
        double diflon = Math.toRadians(lon2 - lon1);

        double a = atan2(diflon, diflat);
        angle = (short) Math.toDegrees(a);

        // Prepocet na kladny uhel
        // funkce atan2 vraci +- podle kvadrantu

        if (angle < 0) {
            angle += 360;
        }

        return angle;
    }

    private double atan2(double y, double x) {
        double z = 0.0;
        double result = 0.0;
        if (x > 0) {
            z = y / x;
            result = arctan(z);
        }
        if (y >= 0 && x < 0) {
            z = y / x;
            result = Math.PI + arctan(z);
        }
        if (y < 0 && x < 0) {
            z = y / x;
            result = -Math.PI + arctan(z);
        }
        if (y > 0 && x == 0) {
            result = Math.PI / 2;
        }
        if (y < 0 && x == 0) {
            result = -Math.PI / 2;
        }
        return result;
    }

    private double arctan(double x) {
        double z = 0;
        if (x < 1) {
            z = x - power(x, 3) / 3 + power(x, 5) / 5 - power(x, 7) / 7 + power(x, 9) / 9 - power(x, 11) / 11 + power(x, 13) / 13;
        } else {
            z = Math.PI / 2 - 1 / x + 1 / (3 * power(x, 3)) - 1 / (5 * power(x, 5)) + 1 / (7 * power(x, 7)) - 1 / (9 * power(x, 9)) + 1 / (11 * power(x, 11)) - 1 / (13 * power(x, 13));
        }
        return z;
    }

    private double power(double x, int y) {
        double result = 1.0;
        for (int i = 0; i < y; i++) {
            result *= x;
        }
        return result;
    }

    /*private double atan2(double y, double x) {
    /*
     * Subject: DSP Trick:  Fixed-pt. Atan2 with Self Normalization
    From: Jim Shima
    Date: 1999/04/23
    Newsgroups: comp.dsp
    THIS WORK IS PLACED IN THE PUBLIC DOMAIN
     *
     *
     * http://dspguru.com/comp.dsp/tricks/alg/fxdatan2.htm
     */

    /*
    double r, angle;
    double coeff_1 = Math.PI / 4;
    double coeff_2 = 3 * coeff_1;
    double abs_y = Math.abs(y) + 1e-15; // kludge to prevent 0/0 condition
    if (x >= 0) {
    r = (x - abs_y) / (x + abs_y);
    angle = 0.1963 * r*r*r - 0.9817 * r + coeff_1;
    } else {
    r = (x + abs_y) / (abs_y - x);
    angle = 0.1963 * r*r*r - 0.9817 * r + coeff_2;
    }
    if (y < 0) {
    return (-angle);     // negate if in quad III or IV
    } else {
    return (angle);
    }
    }*/
}
