<?php

namespace OSM\Element;

use OSM\Element\Member\Way;
use OSM\Point;

class Relation extends Element
{
    public function getOuterWays(): array
    {
        return array_filter($this->members, function ($member) {
            return
                ($member instanceof Way)
                and ('outer' === $member->role)
            ;
        });
    }

    public function getOuterWaysStartingWith(Point $point, ?Way $exclude = null): array
    {
        return array_filter($this->members, function ($member) use ($point, $exclude) {
            return
                ($member instanceof Way)
                and ('outer' === $member->role)
                and $point->isSame($member->getFirstPoint())
                and (
                    is_null($exclude)
                    or !$member->isSame($exclude)
                )
            ;
        });
    }

    public function getOuterWaysStopingWith(Point $point, ?Way $exclude = null): array
    {
        return array_filter($this->members, function ($member) use ($point, $exclude) {
            return
                ($member instanceof Way)
                and ('outer' === $member->role)
                and $point->isSame($member->getLastPoint())
                and (
                    is_null($exclude)
                    or !$member->isSame($exclude)
                )
            ;
        });
    }

    public function getOrderedOuterWays(): array
    {
        $orderedWays = [];
        $ways = $this->getOuterWays();

        $currentWay = reset($ways);
        $veryFirstPoint = $currentWay->getFirstPoint();
        $isDone = false;
        while (!$isDone) {
            $orderedWays[] = $currentWay;
            $nextWays = $this->getOuterWaysStartingWith($currentWay->getLastPoint(), $currentWay);
            assert(count($nextWays) <= 1);
            if (1 === count($nextWays)) {
                $nextWay = reset($nextWays);
                if ($veryFirstPoint->isSame($nextWay->getFirstPoint())) {
                    break;
                }
                $currentWay = $nextWay;
                continue;
            } else {
                $nextWays = $this->getOuterWaysStopingWith($currentWay->getLastPoint(), $currentWay);
                assert(count($nextWays) <= 1);
                if (1 === count($nextWays)) {
                    $nextWay = reset($nextWays);
                    $nextWay->reversePoints();
                    if ($veryFirstPoint->isSame($nextWay->getFirstPoint())) {
                        break;
                    }
                    $currentWay = $nextWay;
                    continue;
                } else {
                    $isDone = true;
                }
            }
        }

        return $orderedWays;
    }
}
