<?php

class ActivityApi extends Api
{
    public $apiName = 'activity';

    public function indexAction()
    {
        return $this->response(
            array(
                'error' => 'Data not found'
            ), 404);
    }

    /**
     * Method GET
     * Get single record (by id)
     * http://DOMAIN/trips/1
     * @return string
     */
    public function viewAction()
    {
        return $this->response(
            array(
                'error'=> 'Data not found'
            ), 404);
    }

    public function createAction()
    {
        syslog(LOG_WARNING,'ActivityApi::createAction()');
        $message = 'We could not find any recent activity in this area';
        $code = 404;
        $time = $this->requestParams['time'] ?? '';
        $location = $this->requestParams['location'] ?? [];
        $country = $this->requestParams['country'] ?? '';
        $member_id = $this->requestParams['member_id'] ?? 0;
        try {
            $db = new Db();

            $country = Geocode::getCountryByGPS($db->getConnect(), $location, $country, Activity::DEFAULT_COUNTRY);
            syslog(LOG_WARNING,'country='.$country);

            $country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');

            if ($country!='US' && $country!='SG') {
                throw new RuntimeException('Trips has not yet launched in your country. You can still track your travel activity and access exclusive deals. Start exploring!',500);
            }
            if ($member_id<1) {
                throw new RuntimeException('Invalid member ID',500);
            }
            if ($time=='' || strtotime($time)<Activity::TIME_LIMIT) { 
                throw new RuntimeException('Invalid time',500);
            }
            $address = NULL;
            $street_address = "";
            syslog(LOG_WARNING, "location => " . json_encode($location));

            if (is_array($location) && isset($location["lat"]) && isset($location["lng"])) {
                // DEBUG
                list($location["lat"],$location["lng"]) = Geocode::mockGPSLocation(
                    $db->getConnect(), $member_id, $location["lat"], $location["lng"], 'default');
                
                syslog(LOG_WARNING,"lat=".$location["lat"].",lng=".$location["lng"]);
                list($street_address,$err) = GeocodeApi::reverseGeocode($db, $location["lat"], $location["lng"]);
                if ($street_address=="") {
                    list($address,$err) = GeofenceApi::getAnchor($db, $location["lat"], $location["lng"]);
                    if (is_array($address) && array_key_exists("address",$address) && $address["address"]!="") {
                        $street_address = $address["address"];
                    }
                }
                if ($street_address=="") {
                    syslog(LOG_WARNING,'Reverse geocoder failed for ('.$location["lat"].', '.$location["lng"].')');
                    throw new RuntimeException($err?"Geocoder failed: $err":'Cannot geocode your GPS location',500);
                }
                syslog(LOG_WARNING,"street_address=".json_encode($street_address));
                if (!is_array($address) || !array_key_exists("address",$address) || $address["address"]=="") {
                    list($address,$err) = Geocode::checkLatLngByAddress($db->getConnect(), $street_address, $country);
                }
                if (!is_array($address) || !isset($address["address"])) {
                    syslog(LOG_WARNING,'Geocoder failed for "'.$street_address.'"');
                    throw new RuntimeException($err?"Geocoder failed: $err":'Cannot get address for your GPS location '.$street_address,500);
                }
                // Adjust GPS coordinates if any
                if (array_key_exists($address["latitude"]) && $address["latitude"]!=null &&
                    array_key_exists($address["longitude"]) && $address["longitude"]!=null &&
                    $address["latitude"]!=0 && $address["longitude"]!=0) {
                    $location["lat"] = $address["latitude"];
                    $location["lng"] = $address["longitude"];
                    $address["lat"] = $address["latitude"];
                    $address["lng"] = $address["longitude"];
                } else if (array_key_exists($address["lat"]) && $address["lat"]!=null &&
                    array_key_exists($address["lng"]) && $address["lng"]!=null &&
                    $address["lat"]!=0 && $address["lng"]!=0) {
                    $location["lat"] = $address["lat"];
                    $location["lng"] = $address["lng"];
                    $address["latitude"] = $address["lat"];
                    $address["longitude"] = $address["lng"];
                }
                list($location,$address) = ActivityApi::adjustAddressLocationGPS($location,$address);
            }
            
            // Step 0. Check if we are in the service area
            /* GeocodeApi::checkWithinTheServiceArea($country,[
                [
                    "type"=>1,
                    "geocode" => [
                        "lat" => $location["lat"],
                        "lng" => $location["lng"]
                    ]
                ]
            ]); //*/
            syslog(LOG_WARNING, "location => " . json_encode($location));

            // Step 1. Get activities
            list($res,$err) = Activity::getActivity(
                $db->getConnect(), $db->getConnectGPS(), $member_id,
                $location["lat"], $location["lng"], Activity::ACTIVITY_RADIUS,
                $time, Activity::TIME_DELTA, $country);
            if (!$res || count($res)<1) {
                if ($err!="") {
                    throw new RuntimeException($err, 500);
                }
                throw new RuntimeException($message, 404);
            }
            syslog(LOG_WARNING,'Recent activity has '.count($res).' trips!');
            //ActivityApi::debugTrips($res,'0');
            $destination_cache = [];
            $result = [];
            $i = 0;
            foreach ($res as $trip) {
                list($trip, $destination_cache) = ActivityApi::processAddressTrip($db, $country, $address, $trip, $destination_cache);
                ActivityApi::debugTrips([$trip],'1');
                if ($trip==NULL) continue;
                $advice = ActivityApi::processAdvice($db, $member_id, $country, $trip);
                ActivityApi::debugTrips([$advice],'2');
                if ($advice && count($advice)>0) {
                    $result[] = $advice;
                    $i++;
                }
                if ($i>=Activity::DEFAULT_OPTIONS) {
                    break;
                }
            }
            $result = ActivityApi::uniqueFilter($db, $result);
            //syslog(LOG_WARNING,json_encode($result));
            ActivityApi::debugTrips($result,'3');
            $result = ActivityApi::processQuotesTrips($db, $member_id, $country, $result);
            ActivityApi::debugTrips($result,'4');
            //$result = ActivityApi::processGojekOption($db, $member_id, $country, $result);
            //ActivityApi::debugTrips($result,'5');
            //syslog(LOG_WARNING,json_encode($result));
            return $this->response(
                array(
                    "trips" => $result,
                    "street_address" => html_entity_decode($street_address)
                ), 200);
        } catch (RuntimeException $e) {
            $message = $e->getMessage();
            $code = $e->getCode();
            syslog(LOG_WARNING,$message);
        }
        return $this->response(
            array(
                "error" => $message
            ), $code);
    }

    private static function adjustAddressLocationGPS($location,$address) {
        syslog(LOG_WARNING,'ActivityApi::adjustAddressLocationGPS($location,$address)');
        if (array_key_exists($address["latitude"]) && $address["latitude"]!=null &&
            array_key_exists($address["longitude"]) && $address["longitude"]!=null &&
            $address["latitude"]!=0 && $address["longitude"]!=0) {
            $location["lat"] = $address["latitude"];
            $location["lng"] = $address["longitude"];
            $address["lat"] = $address["latitude"];
            $address["lng"] = $address["longitude"];
        } else if (array_key_exists($address["lat"]) && $address["lat"]!=null &&
            array_key_exists($address["lng"]) && $address["lng"]!=null &&
            $address["lat"]!=0 && $address["lng"]!=0) {
            $location["lat"] = $address["lat"];
            $location["lng"] = $address["lng"];
            $address["latitude"] = $address["lat"];
            $address["longitude"] = $address["lng"];
        }
        //syslog(LOG_WARNING,json_encode($address));
        //syslog(LOG_WARNING,"location => " . json_encode($location));
        return [$location,$address];
    }

    public static function debugTrips($trips,$what='0') {
        return; /*
        foreach ($trips as $trip) {
            $leg_fare = $trip['multimodal']['options']['leg_fare'];
            syslog(LOG_WARNING,'>>>>>>> '.$what.' >>>>>>> '.json_encode($leg_fare));
        }*/
    }

    public function updateAction()
    {
        return $this->response(
            array(
                "error" => "Update error"
            ), 400);
    }

    public function deleteAction()
    {
        return $this->response(
            array(
                "error" => "Delete error"
            ), 500);
    }

    protected function uniqueFilter($db, $result) {
        syslog(LOG_WARNING,'ActivityApi::uniqueFilter()');
        $cache = [];
        $filtered = [];
        foreach ($result as $trip) {
            list ($cached, $cache) = ActivityApi::checkCache($trip["trip"], $cache);
            if ($cached) {
                $filtered[] = $trip;
            } else {
                syslog(LOG_WARNING,'Duplicate trip? id=' . $trip["trip"]["id"]);
                syslog(LOG_WARNING,json_encode($trip["trip"]));
            }
        }
        if (count($filtered)>0) {
            return $filtered;
        } // Let's hope we will show something...
        return $result;
    }

    protected function checkCache($trip, $cache) {
        syslog(LOG_WARNING,'ActivityApi::checkCache()');
        foreach ($cache as $item) {
            if ($item["id"]>0 && $item["id"]==$trip["id"]) {
                return [false, $cache];
            }
            if ($item["location_end_id"]>0 && $item["location_end_id"]==$trip["location_end_id"]) {
                return [false, $cache];
            }
        }
        $cache[] = $trip;
        return [true, $cache];
    }

    public function processAddressTrip($db, $country, $address, $trip, $cache) {
        syslog(LOG_WARNING,'ActivityApi::processAddressTrip()');
        /*
        {   "id":"10888",
            "travel_date":"2019-08-23 23:54:00",
            "duration":"13",
            "cost_raw":"SGD 18.00",
            "trackedemail_item_id":"2117062",
            "cost":"18.00","updated":
            "2019-08-23 19:31:39.916296",
            "distance":"7.38",
            "transport_provider_id":"3",
            "scheduled":null,
            "travel_date_end":"2019-08-24 00:07:10",
            "dup_id":null,"location_start_id":"523",
            "location_end_id":"42",
            "private":"f",
            "dm":"1434"}
        */
        if (!is_array($address) || !isset($address["lat"]) || !isset($address["lng"])
                || ($address["lat"]==0 && $address["lng"]==0)) {
            return [NULL, $cache];
        }
        if ($address["id"]==$trip["location_start_id"]) {
            list($pass, $cache) = ActivityApi::checkCache($trip, $cache);
            if ($pass) {
                return [$trip, $cache];
            }
        }
        list($trip1,$err) = Trips::tripByLocations($db->getConnect(), $address["id"], $trip["location_start_id"]);
        if (is_array($trip1) && isset($trip1["id"]) && $trip1["id"]>0) {
            list($pass, $cache) = ActivityApi::checkCache($trip1, $cache);
            if ($pass) {
                return [$trip1, $cache];
            }
        }
        $loc_start = Address::getAddressById($db->getConnect(), $trip["location_start_id"]);
        syslog(LOG_WARNING,json_encode($loc_start));
        if (round($address["lat"],3)==round($loc_start["latitude"],3) && round($address["lng"],3)==round($loc_start["longitude"],3)) {
            list($pass, $cache) = ActivityApi::checkCache($trip1, $cache);
            if ($pass) {
                return [$trip1, $cache];
            }
        }
        list($trip2,$err) = Trips::tripByLocations($db->getConnect(), $address["id"], $trip["location_end_id"]);
        if (is_array($trip2) && isset($trip2["id"]) && $trip2["id"]>0) {
            list($pass, $cache) = ActivityApi::checkCache($trip2, $cache);
            if ($pass) {
                return [$trip2, $cache];
            }
        }
        $loc_end = Address::getAddressById($db->getConnect(), $trip["location_end_id"]);
        syslog(LOG_WARNING,json_encode($loc_end));
        if (round($address["lat"],3)==round($loc_end["latitude"],3) && round($address["lng"],3)==round($loc_end["longitude"],3)) {
            list($pass, $cache) = ActivityApi::checkCache($trip2, $cache);
            if ($pass) {
                return [$trip2, $cache];
            }
        }
        // Get distance, duration
        $distance = sprintf("%0.02f",Activity::CITY_CURVATURE * GeocodeApi::distanceBetweenTwoGpsCoordinates($address["lat"],$address["lng"],$loc_start["latitude"],$loc_start["longitude"],'K'));
        $duration = (int)($distance / Activity::CITY_SPEED);
        syslog(LOG_WARNING,"distance1=${distance}");
        syslog(LOG_WARNING,json_encode([$address["lat"],$address["lng"],$loc_start["latitude"],$loc_start["longitude"]]));
        if (Activity::MIN_ACTIVITY_DISTANCE<=$distance && $distance<=Activity::MAX_ACTIVITY_DISTANCE) { // 50m? < x < 50km?
            $trip1 = ActivityApi::createTrip($db, $country, $duration, $distance, $address["id"], $trip["location_start_id"]);
            if (is_array($trip1) && isset($trip1["id"]) && $trip1["id"]>0) {
                list($pass, $cache) = ActivityApi::checkCache($trip1, $cache);
                if ($pass) {
                    return [$trip1, $cache];
                }
            }
        }
        $distance = sprintf("%0.02f",Activity::CITY_CURVATURE * GeocodeApi::distanceBetweenTwoGpsCoordinates($address["lat"],$address["lng"],$loc_end["latitude"],$loc_end["longitude"],'K'));
        $duration = (int)($distance / Activity::CITY_SPEED);
        syslog(LOG_WARNING,"distance2=${distance}");
        syslog(LOG_WARNING,json_encode([$address["lat"],$address["lng"],$loc_end["latitude"],$loc_end["longitude"]]));
        if (Activity::MIN_ACTIVITY_DISTANCE<=$distance && $distance<=Activity::MAX_ACTIVITY_DISTANCE) { // 50m? < x < 50km?
            $trip2 = ActivityApi::createTrip($db, $country, $duration, $distance, $address["id"], $trip["location_end_id"]);
            if (is_array($trip2) && isset($trip2["id"]) && $trip2["id"]>0) {
                list($pass, $cache) = ActivityApi::checkCache($trip2, $cache);
                if ($pass) {
                    return [$trip2, $cache];
                }
            }
        }
        //syslog(LOG_WARNING,json_encode($trip));
        return [NULL, $cache];
    }

    public function createTrip($db, $country, $duration, $distance, $location_start_id, $location_end_id) {
        syslog(LOG_WARNING,"ActivityApi::createTrip(\$db, $country, $duration, $distance, $location_start_id, $location_end_id)");
        $data = [
            'travel_date' => MultiModal::getDate($country),
            'duration' => (int)$duration,
            'cost_raw' => NULL,
            'trackedemail_item_id' => NULL,
            'cost' => NULL,
            'distance' => $distance,
            'transport_provider_id' => NULL,
            'scheduled' => NULL,
            'travel_date_end' => MultiModal::getDate($country),
            'dup_id' => NULL,
            'location_start_id' => $location_start_id,
            'location_end_id' => $location_end_id,
            'private' => 't' /* this is a private entry without the source e-mail */
        ];
        list($id, $err) = Trips::create($db->getConnect(), $data);
        if ($id && $id>0) {
            $trip = Trips::getById($db->getConnect(), $id);
            return $trip;
        }
        return NULL; // WTF?
    }

    public function processAdvice($db, $member_id, $country, $trip) {
        syslog(LOG_WARNING,"ActivityApi::processAdvice(\$db, ${member_id}, ${country}, \$trip)");
        $advice = [
            "trip"=>$trip,
            "scooter"=>[],
            "multimodal"=>[],
            "rideshare"=>[],
            "faster"=>NULL,
            "cheaper"=>NULL
        ];
        $durations = [
            "scooter" => PHP_INT_MAX,
            "multimodal" => PHP_INT_MAX,
            "rideshare" => PHP_INT_MAX
        ];
        try {
            $id = $trip["id"];
            // Get addresses
            $address_start = Address::getAddressById($db->getConnect(), $trip["location_start_id"]);
            $address_end = Address::getAddressById($db->getConnect(), $trip["location_end_id"]);
            if (!$address_start || !$address_end || count($address_start)<3 || count($address_end)<3 ||
                    ($address_start["latitude"]==0 && $address_start["longitude"]==0) ||
                    ($address_end["latitude"]==0 && $address_end["longitude"]==0)) {
                throw new Exception("Invalid address");
            }
            // {"id":"10888","travel_date":"2019-08-23 23:54:00","duration":"13","cost_raw":"SGD 18.00","trackedemail_item_id":"2117062","cost":"18.00","updated":"2019-08-23 19:31:39.916296","distance":"7.38","transport_provider_id":"3","scheduled":null,"travel_date_end":"2019-08-24 00:07:10","dup_id":null,"location_start_id":"523","location_end_id":"42","private":"f","dm":"1434"}
            
            // Step 2. Get advice
            // Step 2.0. Geocode?
            // Step 2.1. Scooter
            $data = []; $options = []; $err = NULL;
            if ($country!='SG') {
                list($data, $options, $err) = ActivityApi::scooter($db, [
                    [
                        "address" => $address_start["address"],
                        "type" => 1,
                        "coordinates" => [
                            "lat" => $address_start["latitude"],
                            "lng" => $address_start["longitude"]
                        ]
                    ],
                    [
                        "address" => $address_end["address"],
                        "type" => 2,
                        "coordinates" => [
                            "lat" => $address_end["latitude"],
                            "lng" => $address_end["longitude"]
                        ]
                    ]
                ], [
                    "travel_time" => true,
                    "quote" => true,
                    "route_overlay" => true
                ]);
            }
            if ($data && count($data)) {
                $advice["scooter"] = ["data"=>$data,"options"=>$options];
                $durations["scooter"] = 0;
                foreach ($data as $step) {
                    $durations["scooter"] += $step["duration"];
                }
            }
            // Step 2.2. Multimodal
            $leg_id = 0;

            $checkOptions = [
                [
                    "type"=>1,
                    "geocode" => [
                        "lat" => $address_start["latitude"],
                        "lng" => $address_end["longitude"]
                    ]
                ]
            ];
            syslog(LOG_WARNING,"ActivityApi: ".json_encode($checkOptions));
            $withinTheServiceArea = GeocodeApi::checkWithinTheServiceArea($country,$checkOptions,false);
            if ($withinTheServiceArea) {
                syslog(LOG_WARNING,"ActivityApi: WITHIN SERVICE AREA!");
                ActivityApi::debugTrips([['multimodal'=>$trip]],'11');
                $trip = MultiModalApi::multimodal(
                    $db, $address_start, $address_end, $country, false/*no cache?*/, $member_id, 
                    MultiModalApi::CACHE_TIMEOUT, $trip["distance"], $trip["duration"]
                );
                ActivityApi::debugTrips([['multimodal'=>$trip]],'12');

                if ($trip && count($trip) && array_key_exists("options",$trip)) {
                    $advice["multimodal"] = $trip;
                    foreach ($trip["options"]["legs"] as $leg) {
                        if ($leg["duration"]<$durations["multimodal"]) {
                            $durations["multimodal"] = $leg["duration"];
                            $leg_id = $leg["id"];
                        }
                    }
                }
            } else {
                syslog(LOG_WARNING,'ActivityApi: IS NOT WITHIN SERVICE AREA!');
                $trip = ActivityApi::createTrip(
                    $db, $country, $trip["duration"], $trip["distance"], 
                    $trip["location_start_id"], $trip["location_end_id"]
                );
            }

            // Step 2.3. Rideshare
            /******* Start GOJEK ******/
            //$trip = MultiModalApi::processGojekOption($db, $member_id, $country, $trip);
            /******* End GOJEK ******/
            if (!$withinTheServiceArea && $country=='US') {
                $trip = MultiModalApi::processRidesharesOption($db, $member_id, $country, $trip);
                syslog(LOG_WARNING,'ActivityApi: >>>>>>> '.json_encode($trip));
                if (is_array($trip) && count($trip)>0 && array_key_exists("options",$trip)) {
                    $advice["rideshare"] = $trip;
                    foreach ($trip["options"]["legs"] as $leg) {
                        if ($leg["duration"]<$durations["multimodal"]) {
                            $durations["rideshare"] = $leg["duration"];
                            $leg_id = $leg["id"];
                        }
                    }
                }
            }

            // Durations...
            foreach ($durations as $key=>$val) {
                if ($val==PHP_INT_MAX) {
                    $durations[$key] = 0;
                }
            }
            // Process advice...
            return $advice;
            // END
        } catch (Exception $e) {
            syslog(LOG_WARNING,$e->getMessage());
        }
        return NULL;
    }

    public function scooter($db, $addresses, $options) {
        syslog(LOG_WARNING,"ActivityApi::scooter()");
        $message = "Failed to run scooter directions";
        try {
            if ($addresses==NULL || !is_array($addresses) || count($addresses)<1 || !isset($addresses[0]["address"])) {
                throw new Exception("Invalid input");
            }
            $result = [];
            $input = [];
            foreach ($addresses as $item) {
                if (isset($item["type"]) && $item["type"]>0 && $item["type"]<3) {
                    $input[$item["type"]] = $item;
                }
            }
            if (isset($input[1]) && isset($input[2]) &&
                isset($input[1]["address"]) && isset($input[2]["address"])) {

                list($result, $err) = ScooterApi::scooter($db, $input);

                if (!is_array($result) || count($result)<1) {
                    throw new Exception($err!=''? $err : 'No scooter option available');
                }
            } else {
                throw new Exception("Invalid address"); 
            }
            if ((isset($options["travel_time"]) && $options["travel_time"]) ||
                (isset($options["quote"]) && $options["quote"]) ||
                (isset($options["route_overlay"]) && $options["route_overlay"])) {
                $options = ScooterApi::options($db, $input, $result, $options);
            }
            return [$result, $options, NULL];
        } catch (Exception $e) {
            syslog(LOG_WARNING,json_encode($e));
            $message = $e->getMessage();
        }
        return [NULL, NULL, $message];
    }

    public static function processQuotesTrips($db, $member_id, $country, $trips) {
        syslog(LOG_WARNING,"ActivityApi::processQuotes(\$db, $member_id, $country, \$trips)");
        if (!is_array($trips) || count($trips)<1) {
            syslog(LOG_WARNING,'Result is missing trips!');
            return $trips; // Whatever it was - not our fault here
        }
        foreach ($trips as $key=>$trip) {
            $trips[$key] = ActivityApi::processQuotesTrip($db, $member_id, $country, $trip);
        }
        return $trips;
    }

    protected static function processQuotesTrip($db, $member_id, $country, $trip) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTrip(\$db, $member_id, $country, \$trip)");
        if (!is_array($trip) || !array_key_exists('multimodal', $trip) || !is_array($trip['multimodal']) || count($trip['multimodal'])<1) {
            syslog(LOG_WARNING,'Trip is missing multimodal!');
            return $trip; // Whatever it was - not our fault here
        }
        // Process multimodal
        $trip['multimodal'] = ActivityApi::processQuotesTripMultimodal($db, $member_id, $country, $trip);
        // Process scooter
        if (array_key_exists('scooter', $trip) && count($trip['scooter'])>0) {
            $trip['scooter'] = ActivityApi::processQuotesTripScooter($db, $member_id, $country, $trip);
        }
        // Process rideshare
        if (array_key_exists('rideshare', $trip) && count($trip['rideshare'])>0) {
            $trip['rideshare'] = ActivityApi::processQuotesTripRideshare($db, $member_id, $country, $trip);
        }
        return $trip;
    }

    protected static function processQuotesTripMultimodal($db, $member_id, $country, $trip) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodal(\$db, $member_id, $country, \$trip)");
        if (!array_key_exists('options',$trip['multimodal']) || count($trip['multimodal']['options'])<1) {
            syslog(LOG_WARNING,'Multimodal trip is missing options!');
            return $trip['multimodal']; // we had checked its existance up the call stack
        }
        $multimodal = $trip['multimodal'];
        $multimodal['options'] = ActivityApi::processQuotesTripMultimodalOptions(
            $db, $member_id, $country, $multimodal['options']);
        return $multimodal;
    }

    public static function processQuotesTripMultimodalOptions($db, $member_id, $country, $options) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalOptions(\$db, $member_id, $country, \$options)");
        if (!is_array($options) || !array_key_exists('legs',$options) || !array_key_exists('leg_steps',$options) 
        || !is_array($options['legs']) || !is_array($options['leg_steps'])
        || count($options['legs'])<1 || count($options['leg_steps'])<1) {
            syslog(LOG_WARNING,'Multimodal trip options is missing data!');
            return $options; // we had checked its existance up the call stack
        }
        $legs = $options['legs'];
        $leg_steps = $options['leg_steps'];
        foreach ($legs as $key=>$leg) {
            $steps = $leg_steps[$leg['id']];
            list($leg, $steps) = ActivityApi::processQuotesTripMultimodalLegSteps($db, $member_id, $country, $leg, $steps);
            $leg_steps[$leg['id']] = $steps;
            $legs[$key] = $leg;
        }
        $options['leg_steps'] = $leg_steps;
        $options['legs'] = $legs;
        return $options;
    }

    protected static function processQuotesTripMultimodalLegSteps($db, $member_id, $country, $leg, $steps) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegSteps(\$db, $member_id, $country, \$leg, \$steps)");
        if (!is_array($leg) || !is_array($steps) || !array_key_exists('id',$leg) || count($steps)<1) {
            syslog(LOG_WARNING,'Multimodal trip option leg is missing data!');
            return [$leg, $steps];
        }
        $total = 0;
        foreach ($steps as $key=>$step) {
            $step = ActivityApi::processQuotesTripMultimodalLegStep($db, $member_id, $country, $step, $leg['id']);
            if (array_key_exists('fare_raw',$step) && $step['fare_raw']>0) {
                $total += $step['fare_raw'];
            }
            $steps[$key] = $step;
        }
        if (!array_key_exists('fare_raw',$leg) || $leg['fare_raw']<$total) {
            $leg['fare_raw'] = $total;
        }
        return [$leg, $steps];
    }

    protected static function processQuotesTripMultimodalLegStep($db, $member_id, $country, $step, $leg_id) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStep(\$db, $member_id, $country, \$step, $leg_id)");
        if (!is_array($step) || !array_key_exists('travel_mode',$step) || ActivityApi::noGPSCoordinates($step)) {
            syslog(LOG_WARNING,'Multimodal trip option leg step is missing data!');
            return $step;
        }
        if ($step['travel_mode']=='WALKING') {
            // Walking is free!
            return $step;
        }
        if ($step['travel_mode']=='BIKE') {
            // Biking is free!
            return $step;
        }
        if ($step['travel_mode']=='NUS_TRANSIT') {
            // NUS shuttle is free!
            return $step;
        }
        if ($step['travel_mode']=='PARK') {
            // I do not know what to do with parking right now
            return $step;
        }
        if ($step['travel_mode']=='SCOOTER') {
            return ActivityApi::processQuotesTripMultimodalLegStepScooter($db, $member_id, $country, $step, $leg_id);
        }
        if ($step['travel_mode']=='TAXI') {
            return ActivityApi::processQuotesTripMultimodalLegStepTaxi($db, $member_id, $country, $step, $leg_id);
        }
        if ($step['travel_mode']=='TRANSIT') {
            return ActivityApi::processQuotesTripMultimodalLegStepTransit($db, $member_id, $country, $step, $leg_id);
        }
        return $step;
    }

    protected static function processQuotesTripMultimodalLegStepScooter($db, $member_id, $country, $step, $leg_id) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepScooter(\$db, $member_id, $country, \$step, $leg_id)");
        
        // TODO: Scooter
        return $step;
    }
    
    protected static function processQuotesTripMultimodalLegStepTaxi($db, $member_id, $country, $step, $leg_id) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTaxi(\$db, $member_id, $country, \$step, $leg_id)");
        // Quote Taxi
        if ($country!='SG') { // We support Singapore/ComfortDelGro only for now!
            return $step; 
        }
        // Do we have a quote already?
        if (array_key_exists('fare_quote',$step) && $step['fare_quote']>0) {
            $step['fare_raw'] = $step['fare_quote'];
            return $step;
        }
        list ($origin, $err) = GeocodeApi::reverseGeocode($db, $step['location_start_lat'], $step['location_start_lng']);
        list ($destination, $err) = GeocodeApi::reverseGeocode($db, $step['location_end_lat'], $step['location_end_lng']);
        $transport_provider_id = Quotes::VENDOR_COMFORTDELGRO; // ComfortDelGro
        $quote_group_id = NULL; // Not groupped 
        $trackedemail_item_id = NULL; // Not from a receipt
        try {
            list($message,$code,$action,$data) = QuoteAPI::createReal(
                $db, $origin, $destination, $member_id, $transport_provider_id, 
                $quote_group_id, $trackedemail_item_id, $country);
            if ($code==200) {
                $load = true;
                if ($action=="view") {
                    $quote = Quotes::getById($db->getConnect(), (int)$data["id"]);
                    if (is_array($quote) && count($quote)>0 && $quote["completed"]!="" && (int)$quote["cost"]!=0) {
                        $load = false;
                        $step['fare_raw'] = (int)$quote["cost"];
                    }
                } 
                if ($load) {
                    // Load
                    for ($i=0; $i<3; $i++) {
                        sleep(1); // wait for a sec
                        $quote = Quotes::getById($db->getConnect(), (int)$data["id"]);
                        if (is_array($quote) && count($quote)>0 && $quote["completed"]!="" && (int)$quote["cost"]!=0) {
                            $step['fare_raw'] = (int)$quote["cost"];
                            break;
                        }
                    }
                }
            }
        } catch (Exception $e) {
            syslog(LOG_WARNING,$e->getMessage());
        }
        return $step;
    }
    
    protected static function processQuotesTripMultimodalLegStepTransit($db, $member_id, $country, $step, $leg_id) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTransit(\$db, $member_id, $country, \$step, $leg_id)");
        // Quote Transit
        if ($country!='SG' && $country!='US') { // We support Singapore & US only for now!
            return $step; 
        }
        if ($country=='US') { // We support BART only
            return ActivityApi::processQuotesTripMultimodalLegStepTransitUS($db, $member_id, $step, $leg_id);
        }
        return ActivityApi::processQuotesTripMultimodalLegStepTransitSG($db, $member_id, $step, $leg_id);
    }

    protected static function processQuotesTripMultimodalLegStepTransitUS($db, $member_id, $step, $leg_id) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTransitUS(\$db, $member_id, \$step, $leg_id)");
        // TODO: !!!
        return $step;
    }

    protected static function processQuotesTripMultimodalLegStepTransitSG($db, $member_id, $step, $leg_id) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTransitSG(\$db, $member_id, \$step, $leg_id)");
        if (array_key_exists('fare_raw',$step) && $step['fare_raw']>0) {
            return $step; // There is a quote already
        }
        $result = MultiModalApi::mytansportsgServiceLeg($leg_id);
        if (is_array($result) && array_key_exists("code",$result) && $result["code"]==0 && array_key_exists("data",$result)) {
            $data = $result["data"];
            if (is_array($data) && array_key_exists($leg_id,$data) && is_array($data[$leg_id]) && array_key_exists('total',$data[$leg_id]) && $data[$leg_id]['total']>0) {
                // Get from the DB!
                list ($res, $err) = Quotes::getLegStepQuoteBySID($db->getConnect(), $step['sid']);
                if (is_array($res) && array_key_exists('fare_raw',$res) && $res['fare_raw']>0) {
                    $step['fare_raw'] = $res['fare_raw'];
                } else {
                    syslog(LOG_WARNING,'No step(sid='.$step['sid'].') quote found in the DB from mytransport.sg service');
                }
            } else {
                syslog(LOG_WARNING,'Invalid response or no quote from mytransport.sg service');
                syslog(LOG_WARNING,json_encode($data));
            }
        } else {
            syslog(LOG_WARNING,'Failed to get the quote from mytransport.sg service');
        }
        return $step;
    }
    
    protected static function noGPSCoordinates($step) {
        syslog(LOG_WARNING,"ActivityApi::noGPSCoordinates(\$step)");
        if (!array_key_exists('location_start_lat',$step) || !array_key_exists('location_start_lng',$step)
        || !array_key_exists('location_end_lat',$step) || !array_key_exists('location_end_lng',$step)
        || ($step['location_start_lat']==NULL &&  $step['location_start_lng']==NULL)
        || ($step['location_end_lat']==NULL &&  $step['location_end_lng']==NULL)
        || ($step['location_start_lat']==0 &&  $step['location_start_lng']==0)
        || ($step['location_end_lat']==0 &&  $step['location_end_lng']==0)        
        ) {
            return true;
        }
        return false;
    }

    protected static function processQuotesTripScooter($db, $member_id, $country, $trip) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripScooter(\$db, $member_id, $country, \$trip)");
        $scooter = $trip['scooter'];

        return $scooter;
    }

    protected static function processQuotesTripRideshare($db, $member_id, $country, $trip) {
        syslog(LOG_WARNING,"ActivityApi::processQuotesTripRideshare(\$db, $member_id, $country, \$trip)");
        $rideshare = $trip['rideshare'];

        return $rideshare;
    }

    public static function processRidesharesOption($db, $member_id, $country, $trips) {
        syslog(LOG_WARNING,"ActivityApi::processRidesharesOption(\$db, $member_id, $country, \$trips)");
        if ($country=='SG') {
            return ActivityApi::processRideshareOption(
                $db, $member_id, $country, $trips, 'Gojek', Quotes::VENDOR_GOJEK, "Asia/Singapore");
        } else if ($country=='US') {
            $tz1 = Address::getTzByAddressId($db->getConnect(),$trips[0]["trip"]["location_start_id"]);
            if ($tz1=="") $tz1 = "America/Los_Angeles";
            $trips_1 = ActivityApi::processRideshareOption(
                $db, $member_id, $country, $trips, 'Uber', Quotes::VENDOR_UBER, $tz1);

            $tz2 = Address::getTzByAddressId($db->getConnect(),$trips[0]["trip"]["location_end_id"]);
            if ($tz2=="") $tz2 = "America/Los_Angeles";
            $trips_2 = ActivityApi::processRideshareOption(
                $db, $member_id, $country, $trips, 'Lyft', Quotes::VENDOR_LYFT, $tz2);

		        $trips_3 = ActivityApi::processRideshareOption(
			        $db, $member_id, $country, $trips, 'Autocab', Quotes::VENDOR_AUTOCAB, $tz1);

            // Basic merge?
            $n1 = count($trips_1) - 1;
            $n2 = count($trips_2) - 1;
            $n3 = count($trips_3) - 1;

            $res = [];
	        if ($n1>=0 && $n2>=0 && $n3>=0) {
		        $n = rand(0,12);
		        //syslog(LOG_WARNING,"ActivityApi rand is ".$n);
		        if ($n<4) {
			        $res[] = $trips_1[$n1];
			        $res[] = $trips_2[$n2];
			        $res[] = $trips_3[$n3];
		        } else if ($n >=4 && $n < 8){
			        $res[] = $trips_2[$n2];
			        $res[] = $trips_3[$n3];
			        $res[] = $trips_1[$n1];
		        } else {
			        $res[] = $trips_3[$n3];
			        $res[] = $trips_1[$n1];
			        $res[] = $trips_2[$n2];
		        }
	        } else if ($n1>=0 && $n2>=0) {
		        $n = rand(0,10);
		        //syslog(LOG_WARNING,"ActivityApi rand is ".$n);
		        if ($n<5) {
			        $res[] = $trips_1[$n1];
			        $res[] = $trips_2[$n2];
		        } else {
			        $res[] = $trips_2[$n2];
			        $res[] = $trips_1[$n1];
		        }
	        } else if ($n1>=0 && $n3>=0) {
		        $n = rand(0,10);
		        //syslog(LOG_WARNING,"ActivityApi rand is ".$n);
		        if ($n<5) {
			        $res[] = $trips_1[$n1];
			        $res[] = $trips_3[$n3];
		        } else {
			        $res[] = $trips_3[$n3];
			        $res[] = $trips_1[$n1];
		        }
	        } else if ($n2>=0 && $n3>=0) {
                $n = rand(0,10);
                //syslog(LOG_WARNING,"ActivityApi rand is ".$n);
                if ($n<5) {
                    $res[] = $trips_2[$n2];
                    $res[] = $trips_3[$n3];
                } else {
                    $res[] = $trips_3[$n3];
                    $res[] = $trips_2[$n2];
                }
            } else if ($n1>=0) {
                $res[] = $trips_1[$n1];
            } else if ($n2>=0) {
                $res[] = $trips_2[$n2];
            } else if ($n3>=0) {
                $res[] = $trips_3[$n3];
            }
            return $res;
        }
        return [];
    }

    public static function processGojekOption($db, $member_id, $country, $trips) {
        syslog(LOG_WARNING,"ActivityApi::processGojekOption(\$db, $member_id, $country, \$trips)");
        return ActivityApi::processRideshareOption(
            $db, $member_id, $country, $trips, 'Gojek', Quotes::VENDOR_GOJEK, "Asia/Singapore");
    }

    public static function processRideshareOption(
        $db, $member_id, $country, $trips, $rideshare, $transport_provider_id, $tz="Asia/Singapore") {
        syslog(LOG_WARNING,"ActivityApi::processRideshareOption(\$db, $member_id, $country, \$trips, $rideshare, $transport_provider_id, $tz)");
        //"trips"=>$trips;
        if (!is_array($trips) && count($trips)<1) {
            syslog(LOG_WARNING,"Invalid trips!");
            return $trips; // No idea how to handle it
        }
        $trip = $trips[0];
        if (!array_key_exists('trip',$trip) || !is_array($trip['trip']) || count($trip['trip'])<10) {
            syslog(LOG_WARNING,"Invalid trip[0]!");
            return $trips; // No idea how to handle it
        }
        $oldTrip = $trip['trip'];
        $newTrip = [
            'cheaper' => [],
            'faster' => [],
            'multimodal' => [],
            'rideshare' => [],
            'scooter' => [],
            'trip' => $trip['trip']
        ];
        $origin = Address::getAddressById($db->getConnect(), $oldTrip['location_start_id']);
        $destination = Address::getAddressById($db->getConnect(), $oldTrip['location_end_id']);

		    $availableTransportProviders = Gis::getCityServicesAvailableForCoordinates($db->getConnect(), $origin["lat"], $origin["lng"],[$transport_provider_id]);
		    if (count($availableTransportProviders) == 0) {
			    $availableTransportProviders = Gis::getCountryServicesAvailableForCoordinates( $db->getConnect(), $origin["lat"], $origin["lng"],[$transport_provider_id]);
		    }
		    if (count($availableTransportProviders) == 0) {
		    	return [];
		    }

        list($res, $err) = GeocodeApi::route($db, 
            $origin['latitude'], $origin['longitude'], 
            $destination['latitude'], $destination['longitude'], 'driving'
        );
        if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
            syslog(LOG_WARNING,'No available routes!');
            syslog(LOG_WARNING,$err);
            return $trips; // We cannot route driving!
        }
        $route_overlay = [];
        $travel_time = PHP_INT_MAX;
        $travel_distance = PHP_INT_MAX;
        foreach ($res["routes"] as $route) {
            $r = GeocodeApi::processRoute($route);
            if ($travel_time>$r['duration']) {
                $travel_time = $r['duration'];
                $travel_distance = $r['distance'];
            } 
            $route_overlay[] = $r;
        }
        $oldTrip["duration"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
        $oldTrip["distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
        /*
            $result = [
            'polyline' => array(), 
            'distance' => NULL, 
            'duration' => NULL, 
            'summary' => $route["summary"], 
            'overview_polyline' => $route["overview_polyline"],
            'bounds' => $route["bounds"]
        ];
        */
        //syslog(LOG_WARNING,json_encode($res));
        $route = $res["routes"][0];
        $route_leg = $route["legs"][0];
        $route_leg_steps = $route_leg["steps"];
        $overview_polyline = $route["overview_polyline"]["points"];

        $leg_steps = [];
        $distance = 0;
        $duration = 0;
        $location_start_lat = 0;
        $location_start_lng = 0;
        $location_end_lat = 0;
        $location_end_lng = 0;
        $polyline = [];
        $i = 0; $j=1; $n = count($route_leg_steps);
        for (; $i<$n; $i++) {
            $step = $route_leg_steps[$i];
            if ($step["travel_mode"] == "DRIVING") {
                $location_start_lat = $step["start_location"]["lat"];
                $location_start_lng = $step["start_location"]["lng"];
                break;
            }
            $leg_steps[] = [
                'distance' => $step["distance"]["value"],
                'duration' => $step["duration"]["value"],
                'html_instructions' => $step["html_instructions"],
                'location_end_lat' => $step["end_location"]["lat"],
                'location_end_lng' => $step["end_location"]["lng"],
                'location_start_lat' => $step["start_location"]["lat"],
                'location_start_lng' => $step["start_location"]["lng"],
                'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
                'sid' => $j++,
                'travel_mode' => $step["travel_mode"]
            ];
        }
        for (;$i<$n; $i++) {
            $step = $route_leg_steps[$i];
            if ($step["travel_mode"] != "DRIVING") {
                break;
            }
            $distance += $step["distance"]["value"];
            $duration += $step["duration"]["value"];
            $location_end_lat = $step["end_location"]["lat"];
            $location_end_lng = $step["end_location"]["lng"];
            $polyline[] = $step["polyline"]["points"];
        }
        $leg_steps[] = [
            'distance' => $distance,
            'duration' => $duration,
            'fare_quote' => null,
            'fare_raw' => null,
            'html_instructions' => "Take ".$rideshare." to ".$destination["address"],
            'location_end_lat' => $location_end_lat,
            'location_end_lng' => $location_end_lng,
            'location_start_lat' => $location_start_lat,
            'location_start_lng' => $location_start_lng,
            'polyline' => MultiModal::processPolyline($polyline),
            'quote_group_id' => null,
            'sid' => $j++,
            'travel_mode' => strtoupper($rideshare)
        ];
        for (;$i<$n; $i++) {
            $step = $route_leg_steps[$i];
            $leg_steps[] = [
                'distance' => $step["distance"]["value"],
                'duration' => $step["duration"]["value"],
                'html_instructions' => $step["html_instructions"],
                'location_end_lat' => $step["end_location"]["lat"],
                'location_end_lng' => $step["end_location"]["lng"],
                'location_start_lat' => $step["start_location"]["lat"],
                'location_start_lng' => $step["start_location"]["lng"],
                'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
                'sid' => $j++,
                'travel_mode' => $step["travel_mode"]
            ];
        }

        list($cost,$quote_id) = QuoteApi::getRideshareQuote(
            $db, $origin["address"], $destination["address"], $member_id, $transport_provider_id, $country);
        $cost = ((int)100*$cost);

        $rideshare = [
            'cost' => $cost,
            'cost_raw' => $cost,
            'count' => 1,
            'country' => $country,
            'distance' => $oldTrip['distance'],
            'dup_id' => null,
            'duration' => $oldTrip['duration'],
            'id' => $oldTrip['id'],
            'location_end' => $destination['address'],
            'location_end_id' => $oldTrip['location_end_id'],
            'location_end_lat' => $destination['latitude'],
            'location_end_lng' => $destination['longitude'],
            'location_end_tz' => $destination['timezone'],
            'location_start' => $origin['address'],
            'location_start_id' => $oldTrip['location_start_id'],
            'location_start_lat' => $origin['latitude'],
            'location_start_lng' => $origin['longitude'],
            'location_start_tz' => $origin['timezone'],
            'member_id' => $member_id,
            'options' => [],
            'parsedemail_item_id' => null, /* $trip['id'] ??? */
            'private' => 't',
            'scheduled' => null,
            'trackedemail_item_id' => null,
            'transport_provider_id' => $transport_provider_id,
            'travel_date' => $oldTrip['travel_date'],
            'travel_date_end' => $oldTrip['travel_date_end'],
            'updated' => $oldTrip['updated']
        ];
        $leg_id = rand(1,10000); // virtual temporary...
        $leg_fare = $cost;
        // Get departure and arrival times...
        $date = new DateTime("now", new DateTimeZone($tz));
        $date->add(new DateInterval('PT15M'));
        $departure_time = $date->getTimestamp();
        $duration = $oldTrip['duration']>0 ? $oldTrip['duration'] : 15;
        $date->add(new DateInterval('PT'.$duration.'M'));
        $arrival_time = $date->getTimestamp();

        $leg = [
            'id' => $leg_id,
            'arrival_time' => $arrival_time,
            'arrival_time_zone' => $destination['timezone'],
            'arrival_timezone' => $tz,
            'departure_time' => $departure_time,
            'departure_time_zone' => $origin['timezone'],
            'departure_timezone' => $tz,
            'distance' => $oldTrip['distance'],
            'duration' => $oldTrip['duration'],
            'fare_raw' => $cost,
            'polyline' => $overview_polyline,
            'quote' => $cost,
            'steps' => count($leg_steps),
        ];
        $options = [
            'gid' => $leg_id, /* ??? */
            'leg_fare' => [$leg_id => $leg_fare],
            'leg_steps' => [$leg_id => $leg_steps],
            'legs' => [$leg],
            'routes' => 1
        ];
        $rideshare['options'] = $options;
        $newTrip['rideshare'] = $rideshare;
        if (count($trips)>2) {
            array_shift($trips); // Remove the first element
        }
        $trips[] = $newTrip;
        syslog(LOG_WARNING,"ActivityApi: ".json_encode($trips));
        return $trips;
    }
}
