public TrainMovementResult GetTrainMovementById(string trainUid, DateTime date)
        {
            // activated schedule may have been deleted by the daily schedule update
            // so include deleted ones
            const string sql = @"
                SELECT [ScheduleId]
                    FROM [ScheduleTrain]
                    WHERE
                        [TrainUid] = @trainUid
                        AND @date >= [StartDate]
                        AND @date <= [EndDate]
                        AND [Runs{0}] = 1
                    ORDER BY [Deleted], [STPIndicatorId], [PowerTypeId] DESC";

            using (DbConnection dbConnection = CreateAndOpenConnection())
            {
                IEnumerable<Guid> scheduleIds = Query<Guid>(string.Format(sql, date.DayOfWeek), new { trainUid, date });

                // the current schedule will be the first
                Guid scheduleId = scheduleIds.FirstOrDefault();

                if (scheduleId != Guid.Empty)
                {
                    TrainMovementResult result = new TrainMovementResult();
                    result.Schedule = GetSchedules(scheduleIds, date).FirstOrDefault();
                    result.Actual = GetActualSchedule(scheduleIds, date.Date, date.Date.Add(new TimeSpan(23, 59, 59))).FirstOrDefault();
                    if (result.Actual != null)
                    {
                        var actualIds = new[] { result.Actual.Id };
                        result.Cancellations = GetCancellations(actualIds, dbConnection);
                        result.Reinstatements = GetReinstatements(actualIds, dbConnection);
                        result.ChangeOfOrigins = GetChangeOfOrigins(actualIds, dbConnection);
                    }
                    else
                    {
                        result.Cancellations = Enumerable.Empty<ExtendedCancellation>();
                        result.Reinstatements = Enumerable.Empty<Reinstatement>();
                        result.ChangeOfOrigins = Enumerable.Empty<ChangeOfOrigin>();
                    }

                    return result;
                }
            }

            return null;
        }
        private IHttpActionResult FromResults(TrainMovementResult result, bool returnTiplocs = true)
        {
            if (result == null)
                return Ok();

            SingleTrainMovementResult actual = new SingleTrainMovementResult
            {
                Movement = result
            };
            ICollection<StationTiploc> tiplocs = null;
            if (returnTiplocs)
                tiplocs = new HashSet<StationTiploc>();
            if (result.Actual != null)
            {
                if (result.Actual.ScheduleOrigin != null)
                {
                    if (returnTiplocs)
                    {
                        tiplocs.Add(result.Actual.ScheduleOrigin);
                    }
                    result.Actual.ScheduleOriginStanoxCode = result.Actual.ScheduleOrigin.Stanox;
                }
                if (result.Actual.Stops != null && result.Actual.Stops.Any())
                {
                    result.Actual.Stops = result.Actual.Stops.Select(s =>
                    {
                        if (s.Tiploc != null)
                        {
                            if (returnTiplocs)
                            {
                                tiplocs.Add(s.Tiploc);
                            }
                            s.TiplocStanoxCode = s.Tiploc.Stanox;
                        }
                        return s;
                    });
                }
            }

            if (result.Schedule != null && result.Schedule.Stops != null && result.Schedule.Stops.Any())
            {
                result.Schedule.Stops = result.Schedule.Stops.Select(s =>
                {
                    if (s.Tiploc != null)
                    {
                        if (returnTiplocs)
                        {
                            tiplocs.Add(s.Tiploc);
                        }
                        s.TiplocStanoxCode = s.Tiploc.Stanox;
                    }
                    return s;
                });
            }

            if (result.Cancellations != null && result.Cancellations.Any())
            {
                result.Cancellations = result.Cancellations.Select(c =>
                {
                    if (c.CancelledAt != null)
                    {
                        if (returnTiplocs)
                        {
                            tiplocs.Add(c.CancelledAt);
                        }
                        c.CancelledAtStanoxCode = c.CancelledAt.Stanox;
                    }
                    return c;
                });
            }

            if (result.ChangeOfOrigins != null && result.ChangeOfOrigins.Any())
            {
                result.ChangeOfOrigins = result.ChangeOfOrigins.Select(c =>
                {
                    if (c.NewOrigin != null)
                    {
                        if (returnTiplocs)
                        {
                            tiplocs.Add(c.NewOrigin);
                        }
                        c.NewOriginStanoxCode = c.NewOrigin.Stanox;
                    }
                    return c;
                });
            }

            if (result.Reinstatements != null && result.Reinstatements.Any())
            {
                result.Reinstatements = result.Reinstatements.Select(r =>
                {
                    if (r.NewOrigin != null)
                    {
                        if (returnTiplocs)
                        {
                            tiplocs.Add(r.NewOrigin);
                        }
                        r.NewOriginStanoxCode = r.NewOrigin.Stanox;
                    }
                    return r;
                });
            }
            actual.Tiplocs = tiplocs ?? Enumerable.Empty<StationTiploc>();

            return Ok(actual);
        }