public IReadOnlyCollection<Itinerary> CalculateItineraries(Route route, IReadOnlyCollection<Voyage> voyages)
        {
            var voyageIds = (
                from v in voyages
                from c in v.Schedule.CarrierMovements
                select new { VoyageId = v.Id, CarrierMovementId = c.Id }
                )
                .ToDictionary(a => a.CarrierMovementId, a => a.VoyageId);

            var paths = CalculatePaths(route, voyages.Select(v => v.Schedule));

            var itineraries = paths
                .Select(p => new Itinerary(p.CarrierMovements.Select(m => new TransportLeg(TransportLegId.New, m.DepartureLocationId, m.ArrivalLocationId, m.DepartureTime, m.ArrivalTime, voyageIds[m.Id], m.Id))))
                .ToList();

            return itineraries;
        }
        public async Task<CargoId> BookCargoAsync(Route route, CancellationToken cancellationToken)
        {
            var cargoId = CargoId.New;
            await _commandBus.PublishAsync(new CargoBookCommand(cargoId, route), cancellationToken).ConfigureAwait(false);

            var itineraries = await _routingService.CalculateItinerariesAsync(route, cancellationToken).ConfigureAwait(false);

            var itinerary = itineraries.FirstOrDefault();
            if (itinerary == null)
            {
                throw DomainError.With("Could not find itinerary");
            }

            await _commandBus.PublishAsync(new CargoSetItineraryCommand(cargoId, itinerary), cancellationToken).ConfigureAwait(false);

            return cargoId;
        }
        private static IEnumerable<Path> CalculatePaths(Route route, IEnumerable<Schedule> schedules)
        {
            var graph = new Graph();
            foreach (var carrierMovement in schedules.SelectMany(s => s.CarrierMovements))
            {
                graph.Add(carrierMovement);
            }

            var paths = new List<Path>
            {
                new Path(0.0, route.DepartureTime, graph.Nodes[route.OriginLocationId.Value])
            };

            var possiblePaths = new List<Path>();

            while (true)
            {
                if (!paths.Any())
                {
                    return possiblePaths.OrderBy(p => p.Distance);
                }

                var orderedPaths = paths
                    .Where(p => !double.IsPositiveInfinity(p.Distance))
                    .OrderBy(p => p.Distance);
                paths = new List<Path>();

                foreach (var path in orderedPaths)
                {
                    if (path.CurrentNode.Name == route.DestinationLocationId.Value)
                    {
                        possiblePaths.Add(path);
                        continue;
                    }

                    paths.AddRange(path.CurrentNode.Edges.Select(e => CreatePath(route, path, e.CarrierMovement, e.Target)).Where(p => p != null));
                }
            }
        }
 public async Task<IReadOnlyCollection<Itinerary>> CalculateItinerariesAsync(Route route, CancellationToken cancellationToken)
 {
     var schedules = await _queryProcessor.ProcessAsync(new GetAllVoyagesQuery(), cancellationToken).ConfigureAwait(false);
     return CalculateItineraries(route, schedules);
 }
        private static Path CreatePath(Route route, Path currentPath, CarrierMovement carrierMovement, Node target)
        {
            if (currentPath.CurrentTime.IsAfter(carrierMovement.DepartureTime))
            {
                return null;
            }
            if (carrierMovement.ArrivalTime.IsAfter(route.ArrivalDeadline))
            {
                return null;
            }

            var distance = (carrierMovement.ArrivalTime - currentPath.CurrentTime).TotalHours;

            return currentPath.AppendAndCreate(
                distance,
                carrierMovement.ArrivalTime,
                target,
                carrierMovement);
        }