Lines 95.00% 19 / 20
Methods 85.71% 6 / 7
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 getLocation 100.00% 1 / 1 100.00% 1 / 1 1
 findNextEvent 88.88% 8 / 9 0.00% 0 / 1 4.02
 getDayOn 100.00% 6 / 6 100.00% 1 / 1 3
 getPositionAt 100.00% 1 / 1 100.00% 1 / 1 1
 getCurrentPosition 100.00% 1 / 1 100.00% 1 / 1 1
 isSunUp 100.00% 1 / 1 100.00% 1 / 1 1
17final readonly class LocatedSunCalendar implements SunCalendar
18{
19    // Polar night can hide an event for months, but every event a location ever sees recurs within a year.
20    private const int SEARCHED_DAYS = 368;
21
22    public function __construct(
23        private GeoLocation $location,
24        private Clock $clock,
25        private NoaaSolarCalculator $calculator,
26    ) {}
27
28    public function getLocation(): GeoLocation
29    {
30        return $this->location;
31    }
32
33    public function findNextEvent(SunEvent $event, ?SunOffset $offset = null, ?Instant $after = null): ?Instant
34    {
35        $offset ??= SunOffset::none();
36        $after ??= $this->clock->getNow();
37        $day = $after->minus($offset->getMagnitude())->toDateTime($this->clock->getTimeZone())->modify('-1 day');
38
39        for ($searched = 0; $searched < self::SEARCHED_DAYS; ++$searched) {
40            $eventTime = $this->calculator->findEventOn($day, $this->location, $event);
41
42            if ($eventTime !== null && $offset->applyTo($eventTime)->isAfter($after)) {
43                return $offset->applyTo($eventTime);
44            }
45
46            $day = $day->modify('+1 day');
47        }
48
49        return null;
50    }
51
52    public function getDayOn(DateTimeImmutable $day): SunDay
53    {
54        $sunDay = SunDay::forDate($day);
55
56        foreach (SunEvent::cases() as $event) {
57            $eventTime = $this->calculator->findEventOn($day, $this->location, $event);
58
59            if ($eventTime !== null) {
60                $sunDay = $sunDay->withEventTime($event, $eventTime);
61            }
62        }
63
64        return $sunDay;
65    }
66
67    public function getPositionAt(Instant $moment): SunPosition
68    {
69        return $this->calculator->computePositionAt($moment, $this->location);
70    }
71
72    public function getCurrentPosition(): SunPosition
73    {
74        return $this->getPositionAt($this->clock->getNow());
75    }
76
77    public function isSunUp(): bool
78    {
79        return $this->calculator->isSunUpAt($this->clock->getNow(), $this->location);
80    }
81}