<?php



const DEGREE_OF_CIRCLE = 360;

/**
 * NOT USED
 *
 * @param $utmCoordPointOfView
 * @param $utmCoordPointToBeViewed
 * @return float
 */
function getDistanceWithUTM($utmCoordPointOfView, $utmCoordPointToBeViewed)
{
    $diffX = $utmCoordPointToBeViewed['ew'] - $utmCoordPointOfView['ew'];
    $diffY = $utmCoordPointToBeViewed['nw'] - $utmCoordPointOfView['nw'];
    $pythagoras = sqrt($diffX * $diffX + $diffY * $diffY);
    return $pythagoras;
}

/**
 * Returns a factor c which describes the shortest distance on the earth between two points as a factor
 *
 * @param $lon1
 * @param $lat1
 * @param $lon2
 * @param $lat2
 * @return factor c
 */
function getGeodistWithGeo($lon1, $lat1, $lon2, $lat2)
{
    $lon1_new = deg2rad($lon1);
    $lat1_new = deg2rad($lat1);
    $lon2_new = deg2rad($lon2);
    $lat2_new = deg2rad($lat2);
    $c = acos(sin($lat1_new) * sin($lat2_new) + cos($lat1_new) * cos($lat2_new) * cos($lon2_new - $lon1_new));
    return $c;
}


/*
 * Converts the distance factor c to the distance in meters
 *
 * @param factor c
 * @return distance in meter
 */
function getDistanceWithGeodist($c)
{
    return $c * 6378388;
}

/*
 * Calculators the "azimuth" angle (also called "grid bearing") between two points.
 *
 * @param factor c
 * @param $lon1
 * @param $lat1
 * @param $lon2
 * @param $lat2
 * @return azimuth
 */
function getAzimuthAngleWithGeoAndGeodist($c, $lon1, $lat1, $lon2, $lat2)
{
    $lat1_in_rad = deg2rad($lat1);
    $lat2_in_rad = deg2rad($lat2);
    $denominator = (cos($lat1_in_rad) * sin($c));

    // avoid division with 0
    if ($denominator == 0) {
        $denominator = 0.00000000001;

    }
    $angle = acos((sin($lat2_in_rad) - sin($lat1_in_rad) * cos($c)) / $denominator);
    if ($lon1 < $lon2) {
        return rad2deg($angle);
    } else {
        return DEGREE_OF_CIRCLE - rad2deg($angle);
    }

}


/**
 * NOT USED
 *
 * @param $lw
 * @param $bw
 * @return array
 */
function geo2utm($lw, $bw)
{
    /* Copyright (c) 2006, HELMUT H. HEIMEIER
       Permission is hereby granted, free of charge, to any person obtaining a
       copy of this software and associated documentation files (the "Software"),
       to deal in the Software without restriction, including without limitation
       the rights to use, copy, modify, merge, publish, distribute, sublicense,
       and/or sell copies of the Software, and to permit persons to whom the
       Software is furnished to do so, subject to the following conditions:
       The above copyright notice and this permission notice shall be included
       in all copies or substantial portions of the Software.*/

    /* Die Funktion wandelt geographische Koordinaten in UTM Koordinaten
       um. Geographische Länge lw und Breite bw müssen im WGS84 Datum
       gegeben sein. Berechnet werden UTM Zone, Ostwert ew und Nordwert nw.*/


    //Geographische Länge lw und Breite bw im WGS84 Datum
    if ($lw == "" || $bw == "") {
        $zone = "";
        $ew = "";
        $nw = "";
        array("zone" => $zone, "ew" => $ew, "nw" => $nw);
    }
    if ($lw <= -180 || $lw > 180 || $bw <= -80 || $bw >= 84) {
        echo("Werte nicht im Bereich des UTM Systems\n" +
            "-180° <= LW < +180°, -80° < BW < 84° N");
        $zone = "";
        $ew = "";
        $nw = "";
        array("zone" => $zone, "ew" => $ew, "nw" => $nw);
    }
    $lw = doubleval($lw);
    $bw = doubleval($bw);

    //WGS84 Datum
    //Große Halbachse a und Abplattung f
    $a = 6378137.000;
    $f = 3.35281068e-3;
    $b_sel = 'CDEFGHJKLMNPQRSTUVWXX';

    //Polkrümmungshalbmesser c
    $c = $a / (1 - $f);

    //Quadrat der zweiten numerischen Exzentrizität
    $ex2 = (2 * $f - $f * $f) / ((1 - $f) * (1 - $f));
    $ex4 = $ex2 * $ex2;
    $ex6 = $ex4 * $ex2;
    $ex8 = $ex4 * $ex4;

    //Koeffizienten zur Berechnung der Meridianbogenlänge
    $e0 = $c * (pi() / 180) * (1 - 3 * $ex2 / 4 + 45 * $ex4 / 64 - 175 * $ex6 / 256 + 11025 * $ex8 / 16384);
    $e2 = $c * (-3 * $ex2 / 8 + 15 * $ex4 / 32 - 525 * $ex6 / 1024 + 2205 * $ex8 / 4096);
    $e4 = $c * (15 * $ex4 / 256 - 105 * $ex6 / 1024 + 2205 * $ex8 / 16384);
    $e6 = $c * (-35 * $ex6 / 3072 + 315 * $ex8 / 12288);

    //Längenzone lz und Breitenzone (Band) bz
    $lzn = intval(($lw + 180) / 6) + 1;
    $lz = "$lzn";
    if ($lzn < 10) $lz = "0" + $lzn;
    $bd = intval(1 + ($bw + 80) / 8);
    $bz = substr($b_sel, $bd - 1, 1);

    //Geographische Breite in Radianten br
    $br = $bw * pi() / 180;

    $tan1 = tan($br);
    $tan2 = $tan1 * $tan1;
    $tan4 = $tan2 * $tan2;

    $cos1 = cos($br);
    $cos2 = $cos1 * $cos1;
    $cos4 = $cos2 * $cos2;
    $cos3 = $cos2 * $cos1;
    $cos5 = $cos4 * $cos1;

    $etasq = $ex2 * $cos2;

    //Querkrümmungshalbmesser nd
    $nd = $c / sqrt(1 + $etasq);

    //Meridianbogenlänge g aus gegebener geographischer Breite bw
    $g = ($e0 * $bw) + ($e2 * sin(2 * $br)) + ($e4 * sin(4 * $br)) + ($e6 * sin(6 * $br));

    //Längendifferenz dl zum Bezugsmeridian lh
    $lh = ($lzn - 30) * 6 - 3;
    $dl = ($lw - $lh) * pi() / 180;
    $dl2 = $dl * $dl;
    $dl4 = $dl2 * $dl2;
    $dl3 = $dl2 * $dl;
    $dl5 = $dl4 * $dl;

    //Maßstabsfaktor auf dem Bezugsmeridian bei UTM Koordinaten m = 0.9996
    //Nordwert nw und Ostwert ew als Funktion von geographischer Breite und Länge

    if ($bw < 0) {
        $nw = 10e6 + 0.9996 * ($g + $nd * $cos2 * $tan1 * $dl2 / 2 +
                $nd * $cos4 * $tan1 * (5 - $tan2 + 9 * $etasq) * $dl4 / 24);
    } else {
        $nw = 0.9996 * ($g + $nd * $cos2 * $tan1 * $dl2 / 2 +
                $nd * $cos4 * $tan1 * (5 - $tan2 + 9 * $etasq) * $dl4 / 24);
    }
    $ew = 0.9996 * ($nd * $cos1 * $dl + $nd * $cos3 * (1 - $tan2 + $etasq) * $dl3 / 6 +
            $nd * $cos5 * (5 - 18 * $tan2 + $tan4) * $dl5 / 120) + 500000;

    $zone = $lz . $bz;

    $nk = $nw - intval($nw);
    if ($nk < 0.5) $nw = "" . intval($nw);
    else $nw = "" . (intval($nw) + 1);

    while (strlen($nw) < 7) {
        $nw = "0" . $nw;
    }

    $nk = $ew - intval($ew);
    if ($nk < 0.5) $ew = "0" . intval($ew);
    else $ew = "0" . intval($ew + 1);

    return array("zone" => $zone, "ew" => $ew, "nw" => $nw);
}

/**
 * NOT USED
 *
 * @param $utmCoordPointOfView
 * @param $utmCoordPointToBeViewed
 * @return float|number
 */
function getDestinationAngle($utmCoordPointOfView, $utmCoordPointToBeViewed)
{

    $diffX = $utmCoordPointToBeViewed['ew'] - $utmCoordPointOfView['ew'];
    $diffY = $utmCoordPointToBeViewed['nw'] - $utmCoordPointOfView['nw'];

    // diffX is "Gegenkathete"
    if (abs($diffX) > abs($diffY)) {
        if ($diffY == 0) {
            if ($diffX > 0) {
                return 90;
            } else {
                return 270;
            }
        }
        $angle = rad2deg(atan($diffX / $diffY));
    } else {//diffY is "Gegenkathete"
        if ($diffX == 0) {
            if ($diffY > 0) {
                return 0;
            } else {
                return 180;
            }
        }
        $angle = rad2deg(atan($diffY / $diffX));
    }
    if ($diffX > 0 && $diffY < 0) {
        $angle = abs($angle) + 90;
    } else if ($diffX < 0 && $diffY < 0) {
        $angle = abs($angle) + 180;
    } else if ($diffX < 0 && $diffY > 0) {
        $angle = abs($angle) + 270;
    }

    return $angle;
}

/**
 * calculates the weighted average angle between two given angles and a given weight-factor
 *
 * @param $weight_factor
 * @param $angle1
 * @param $angle2
 * @return weighted average angle
 */
function weightedAverageAngle($weight_factor, $angle1, $angle2)
{
    if ($angle1 == $angle2) {
        return $angle1;
    } else if ($angle2 < $angle1) {
        $angle_temp = $angle1;
        $angle1 = $angle2;
        $angle2 = $angle_temp;
    }

    if (($angle2 - $angle1) < 180) {
        return $angle1 + ($angle2 - $angle1) * $weight_factor;
    } else {
        return ($angle2 + (DEGREE_OF_CIRCLE - $angle2 + $angle1) * $weight_factor) % DEGREE_OF_CIRCLE;
    }

}


/**
 * This function calculates the interpolated point according a given timestamp which has to be between the timestamp of the first point (lower_bound) and the timestamp of the second point (upper_bound).
 *
 * @param $lower_bound
 * @param $upper_bound
 * @param $timestamp
 * @return array
 */
function InterpolatePointsAtTimestamp($lower_bound, $upper_bound, $timestamp)
{
    $deltaTime = $upper_bound[TIMESTAMP] - $lower_bound[TIMESTAMP];
    $deltaX = $upper_bound[PLNG] - $lower_bound[PLNG];
    $deltaY = $upper_bound[PLAT] - $lower_bound[PLAT];

    $factor = ($timestamp - $lower_bound[TIMESTAMP]) / $deltaTime;
    $newTimestamp = $timestamp + round($factor * $deltaTime);
    $newX = $lower_bound[PLNG] + ($factor * $deltaX);
    $newY = $lower_bound[PLAT] + ($factor * $deltaY);
    $newAngle = weightedAverageAngle($factor, $lower_bound[THETAX], $upper_bound[THETAX]);

    $return_arr = [PLAT => $newY, PLNG => $newX, TIMESTAMP => $newTimestamp, THETAX => $newAngle, ALPHA => $lower_bound[ALPHA], VIDEOID => $lower_bound[VIDEOID]];
    return $return_arr;

}


/**
 * This function perfomrs the whole check if a viewed Point intersects with the viewing angle of another point.
 * If this intersection check is correct then the viewing point will be added tp the given result_arr.
 *
 * @param $lon1
 * @param $lat1
 * @param $lon2
 * @param $lat2
 * @param $pointOfView
 * @param $result_arr
 */
function performPointIntersectsViewingAreaCheck($pointOfView, $viewedPoint, &$result_arr)
{
    $lon1 = $viewedPoint[PLNG];
    $lat1 = $viewedPoint[PLAT];

    $lon2 = $pointOfView[PLNG];
    $lat2 = $pointOfView[PLAT];


    $c = getGeodistWithGeo($lon1, $lat1, $lon2, $lat2); // result is an angle (Bogenmaß)
    $distance = getDistanceWithGeodist($c); // result is in Meter

    if (DEBUGGING_MODE){
        echo "-------------------------------------------------------------------------</br>";
        echo "Distance $distance</br>";
    }

    // check if the distance between the two points is inside in the configured viewing distance. if not --> return
    if ($distance <= VIEWING_DISTANCE) {
        if (DEBUGGING_MODE) {
            echo "lon1: ".$lon1 ." lat1: ".$lat1."    lon2: ".$lon2." lat2: ".$lat2."      ".$distance."     TethaX: ".$pointOfView[THETAX] ."</br>";
        }


        $azimuth = getAzimuthAngleWithGeoAndGeodist($c, $lon2, $lat2, $lon1, $lat1);

        $alpha = $pointOfView[ALPHA];
        $viewAngleMin = ($pointOfView[THETAX] + DEGREE_OF_CIRCLE - $alpha / 2) % DEGREE_OF_CIRCLE;
        $viewAngleMax = ($pointOfView[THETAX] + $alpha / 2) % DEGREE_OF_CIRCLE;

        if (DEBUGGING_MODE) {
            echo "Inside Distance $distance</br>";
            echo "lon1: ".$lon1 ." lat1: ".$lat1."    lon2: ".$lon2." lat2: ".$lat2."      ".$distance."     TethaX: ".$pointOfView[THETAX] ."</br>";
            echo "$viewAngleMin - $viewAngleMax  ------ $azimuth </br>";
        }

        // determine if the azimuth between the two points is between the viewing angle of the point of view
        if ($viewAngleMax > $viewAngleMin) {
            $angleInsideViewangle = $azimuth > $viewAngleMin && $azimuth < $viewAngleMax;
        } else {
            $angleInsideViewangle = $azimuth > $viewAngleMin || $azimuth < $viewAngleMax;
        }

        if ($angleInsideViewangle) {

            $ts = $pointOfView[TIMESTAMP];

            if (!isset($result_arr[$pointOfView[VIDEOID]])) {
                if (DEBUGGING_MODE){
                    echo "Setting </br>";
                }
                $result_arr[$pointOfView[VIDEOID]] = array('value' => $pointOfView[VIDEOID], 'mintimespan' => $ts, 'maxtimespan' => $ts);
            } else if ($result_arr[$pointOfView[VIDEOID]]['mintimespan'] > $ts) {
                $result_arr[$pointOfView[VIDEOID]]['mintimespan'] = $ts;

            } else if ($result_arr[$pointOfView[VIDEOID]]['maxtimespan'] < $ts) {
                $result_arr[$pointOfView[VIDEOID]]['maxtimespan'] = $ts;
            }
        }
    }
}
