<?php

class MultiModalFilter {

    const NO_FILTER_COUNT = 3;

    // https://www.php.net/manual/en/function.uasort.php
    public static function sortTrip($trip) {
        if (is_array($trip) && array_key_exists('options',$trip) && array_key_exists('legs',$trip['options']) && is_array($trip['options']['legs'])) {
            $legs = $trip['options']['legs'];
            $sorted_legs = [];
            $sorted_fare = [];
            foreach ($legs as $leg) {
                $sorted_legs[$leg['id']] = $leg;
                $sorted_fare[$leg['id']] = $leg['fare_raw'];
            }
            asort($sorted_fare);
            $legs = [];
            foreach ($sorted_fare as $leg_id=>$fare_raw) {
                $legs[] = $sorted_legs[$leg_id];
            }
            $trip['options']['legs'] = $legs;
        }
        return $trip;
    }

    public static function byDuration($trip) {
        syslog(LOG_WARNING,'MultiModalFilter::byDuration()');
        if (MultiModalFilter::sanityCheck($trip)) {
            syslog(LOG_WARNING,'Sanity check failed!');
            return $trip; // I do not know how to handle it or not many options
        }
        // Taxi
        $taxies = MultiModalFilter::getTravelModeRoutes($trip,"TAXI",["NUS_TRANSIT"]);
        if (is_array($taxies) && count($taxies)>1) {
            list($good,$bad) = MultiModalFilter::getFastest($taxies);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // NUS
        $nus = MultiModalFilter::getTravelModeRoutes($trip,"NUS_TRANSIT",["TAXI"]);
        if (is_array($nus) && count($nus)>1) {
            list($good,$bad) = MultiModalFilter::getFastest($nus);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // Scooter
        $scooters = MultiModalFilter::getTravelModeRoutes($trip,"SCOOTER");
        if (is_array($scooters) && count($scooters)>1) {
            list($good,$bad) = MultiModalFilter::getFastest($scooters);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // bikes
        $bikes = MultiModalFilter::getTravelModeRoutes($trip,"BIKE");

        if (is_array($bikes) && count($bikes)>1) {
            list($good,$bad) = MultiModalFilter::getFastest($bikes);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // Public
        $transit = MultiModalFilter::getTravelModeRoutes($trip,"TRANSIT",["TAXI","NUS_TRANSIT","SCOOTER","BIKE"]);
        if (is_array($transit) && count($transit)>1) {
            list($good,$bad) = MultiModalFilter::getFastest($transit);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        $trip['options']['routes'] = count($trip['options']['legs']);
        return $trip;
    }

    public static function byCost($trip) {
        syslog(LOG_WARNING,'MultiModalFilter::byCost()');
        if (MultiModalFilter::sanityCheck($trip)) {
            syslog(LOG_WARNING,'Sanity check failed!');
            return $trip; // I do not know how to handle it or not many options
        }
        // Taxi
        $taxies = MultiModalFilter::getTravelModeRoutes($trip,"TAXI",["NUS_TRANSIT"]);
        if (is_array($taxies) && count($taxies)>1) {
            list($good,$bad) = MultiModalFilter::getCheapest($trip,$taxies);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // NUS
        $nus = MultiModalFilter::getTravelModeRoutes($trip,"NUS_TRANSIT",["TAXI"]);
        if (is_array($nus) && count($nus)>1) {
            list($good,$bad) = MultiModalFilter::getCheapest($trip,$nus);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // Scooter
        $scooters = MultiModalFilter::getTravelModeRoutes($trip,"SCOOTER");
        if (is_array($scooters) && count($scooters)>1) {
            list($good,$bad) = MultiModalFilter::getCheapest($trip,$scooters);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // Scooter
        $bikes = MultiModalFilter::getTravelModeRoutes($trip,"BIKE");
        if (is_array($bikes) && count($bikes)>1) {
            list($good,$bad) = MultiModalFilter::getCheapest($trip,$bikes);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        // Public
        $transit = MultiModalFilter::getTravelModeRoutes($trip,"TRANSIT",["TAXI","NUS_TRANSIT","SCOOTER","BIKE"]);
        if (is_array($transit) && count($transit)>1) {
            list($good,$bad) = MultiModalFilter::getCheapest($trip,$transit);
            if (is_array($good)) {
                $trip = MultiModalFilter::filterOut($trip, $bad);
            }
        }
        $trip['options']['routes'] = count($trip['options']['legs']);
        $trip = MultiModalFilter::durations($trip);
        return $trip;
    }

    protected static function filterOut($trip, $bad) {
        $badLegs = [];
        foreach ($bad as $item) {
            $leg_id = $item["id"];
            unset($trip['options']['leg_fare'][$leg_id]);
            unset($trip['options']['leg_steps'][$leg_id]);
            $badLegs[] = $leg_id;
        }
        $newLegs = [];
        $legs = $trip['options']['legs'];
        foreach ($legs as $leg) {
            if (in_array($leg['id'],$badLegs)) continue;
            $newLegs[] = $leg;
        }
        $trip['options']['legs'] = $newLegs; 
        return $trip;
    }

    protected static function getCheapest($trip,$what) {
        $cheapest = PHP_INT_MAX;
        $good = NULL;
        $bad = [];
        foreach ($what as $item) {
            $leg_id = $item['id'];
            $fare = $trip['options']['leg_fare'][$leg_id];
            if ($fare!=null && $fare>0 && $fare<$cheapest) {
                $good = $item;
                $cheapest = $fare;
            }
            // Do we do extra checks?
        }
        if (!is_array($good) || !array_key_exists('id',$good)) {
            return [NULL,$what]; // All entries are BAD
        }
        foreach ($what as $item) {
            if ($item['id']!=$good['id']) {
                $bad[] = $item;
            }
        }
        return [$good, $bad];
    }

    protected static function getFastest($what) {
        $fastest = PHP_INT_MAX;
        $good = NULL;
        $bad = [];
        foreach ($what as $item) {
            if ($item['duration']<$fastest) {
                $good = $item;
                $fastest = $item['duration'];
            }
        }
        if (!is_array($good) || !array_key_exists('id',$good)) {
            return [NULL,$what]; // All entries are BAD
        }
        foreach ($what as $item) {
            if ($item['id']!=$good['id']) {
                $bad[] = $item;
            }
        }
        return [$good, $bad];
    }

    protected static function getLegById($trip,$leg_id) {
        foreach ($trip['options']['legs'] as $leg) {
            if ($leg['id']==$leg_id) {
                return $leg;
            }
        }
        return NULL;
    }

    protected static function getTravelModeRoutes($trip,$travel_mode,$exclude=[]) {
        $result = [];
        $excludes = count($exclude);
        $leg_steps = $trip['options']['leg_steps'];
        foreach ($leg_steps as $leg_id=>$steps) {
            foreach ($steps as $step) {
                if ($step['travel_mode']==$travel_mode) {
                    $result[$leg_id] = MultiModalFilter::getLegById($trip,$leg_id);
                    if ($excludes<1) {
                        break;
                    }
                }
                if ($excludes>0 && in_array($step['travel_mode'],$exclude)) {
                    if (array_key_exists($leg_id,$result)) {
                        unset($result[$leg_id]);
                    }
                    break;
                }
            }
        }
        return $result;
    }

    protected static function sanityCheck($trip) {
        return !is_array($trip) || !array_key_exists('options',$trip) || !is_array($trip['options'])
        || !array_key_exists('legs',$trip['options']) || !is_array($trip['options']['legs'])
        || count($trip['options']['legs']) <= MultiModalFilter::NO_FILTER_COUNT;
    }

    protected static function durations($trip) {
        foreach ($trip['options']['leg_steps'] as $leg_id=>$steps) {
            $dirty = false;
            foreach ($steps as $i=>$step) {
                if (!array_key_exists('duration',$step) || $step['duration']<60) {
                    $step['duration'] = 60;
                    $steps[$i] = $step;
                    $dirty = true;
                }
            }
            if ($dirty) {
                $trip['options']['leg_steps'][$leg_id] = $steps;
            }
        }
        return $trip;
    }
}
