private Penalty DistanceInfringementPenalty(double calcDistance, double pctInfringement, string description)
        {
            // Rule 13.3.5

            Penalty penalty;

            if (pctInfringement <= 0)
            {
                penalty = null;
            }
            else if (pctInfringement <= 25)
            {
                penalty = new Penalty(string.Format("R13.3.5: {1} {0:0m} <= 25%", calcDistance, description),
                                      PenaltyType.TaskPoints, (int)(Math.Round(2 * pctInfringement / 0.1, 0)));
            }
            else //if (pctInfringement > 25)
            {
                penalty = new Penalty(Result.NewNoResult(string.Format("R13.3.5: {1} {0:0m} > 25%", calcDistance, description)));
            }

            return(penalty);
        }
        public override void Process()
        {
            base.Process();

            // parse and resolve pilot dependent values
            // the static values are already defined
            // syntax is already checked
            //TODO: apply globally instead of a per task basis
            switch (Definition.ObjectType)
            {
            default:
                throw new ArgumentException("Unknown penalty type '" + Definition.ObjectType + "'");

            case "BPZ":
            {
                var sortedTasks = from obj in Engine.Heap.Values
                                  where obj is ScriptingTask
                                  orderby((ScriptingTask)obj).TaskOrder
                                  select obj as ScriptingTask;

                var firstPoint = Engine.Report.TakeOffPoint;
                var done       = false;
                foreach (var task in sortedTasks)
                {
                    if (done)
                    {
                        break;
                    }

                    var lastPoint = task.Result.LastUsedPoint;
                    if (lastPoint == null)
                    {
                        lastPoint = Engine.Report.LandingPoint;
                        done      = true;
                    }

                    var currentTrack =
                        new Track(Engine.Report.FlightTrack)
                        .Filter(p => p.Time >= firstPoint.Time && p.Time <= lastPoint.Time);
                    var penaltyPoints = area.BpzPenalty(currentTrack);
                    if (penaltyPoints > 0)
                    {
                        var infringement = new Penalty("R7.3.6 " + description, PenaltyType.CompetitionPoints, penaltyPoints);
                        infringement.InfringingTrack = area.FilterTrack(currentTrack);
                        Infringements.Add(infringement);
                        task.Penalties.Add(infringement);
                    }

                    firstPoint = lastPoint;
                }
            }
            break;

            case "RPZ":
            {
                var sortedTasks = from obj in Engine.Heap.Values
                                  where obj is ScriptingTask
                                  orderby((ScriptingTask)obj).TaskOrder
                                  select obj as ScriptingTask;

                var firstPoint = Engine.Report.TakeOffPoint;
                var done       = false;
                foreach (var task in sortedTasks)
                {
                    if (done)
                    {
                        break;
                    }

                    var lastPoint = task.Result.LastUsedPoint;
                    if (lastPoint == null)
                    {
                        lastPoint = Engine.Report.LandingPoint;
                        done      = true;
                    }

                    var currentTrack =
                        new Track(Engine.Report.FlightTrack)
                        .Filter(p => p.Time >= firstPoint.Time && p.Time <= lastPoint.Time);
                    var penaltyPoints = area.RpzPenalty(currentTrack);
                    if (penaltyPoints > 0)
                    {
                        var infringement = new Penalty("R7.3.4 " + description, PenaltyType.CompetitionPoints, penaltyPoints);
                        infringement.InfringingTrack = area.FilterTrack(currentTrack);
                        Infringements.Add(infringement);
                        task.Penalties.Add(infringement);
                    }

                    firstPoint = lastPoint;
                }
            }
            break;

            case "VSMAX":
                //TODO: implement VSMAX
            {
                var sortedTasks = from obj in Engine.Heap.Values
                                  where obj is ScriptingTask
                                  orderby((ScriptingTask)obj).TaskOrder
                                  select obj as ScriptingTask;

                var firstPoint = Engine.Report.TakeOffPoint;
                var done       = false;
                foreach (var task in sortedTasks)
                {
                    if (done)
                    {
                        break;
                    }

                    var lastPoint = task.Result.LastUsedPoint;
                    if (lastPoint == null)
                    {
                        lastPoint = Engine.Report.LandingPoint;
                        done      = true;
                    }

                    var infringingTrack = new Track(Engine.Report.FlightTrack)
                                          .Filter(p => p.Time >= firstPoint.Time && p.Time <= lastPoint.Time)
                                          .FilterPairs((p1, p2) => Math.Abs(Physics.VerticalVelocity(p1, p2)) > maxSpeed)
                                          .FilterSegments((p1, p2) => (p2.Time - p1.Time).TotalSeconds > 15);


                    foreach (var str in infringingTrack.ToStringList())
                    {
                        task.AddNote("Max ascent/descent rate exceeded " + str, true);
                        //task.AddNote(
                        //    string.Format("Max ascent/descent rate exceeded from {0} to {1}: {2:0} ft/min for {3} sec",
                        //    first.ToString(AXPointInfo.Time).TrimEnd(),
                        //    last.ToString(AXPointInfo.Time).TrimEnd(),
                        //    Physics.VerticalVelocity(first, last) * Physics.METERS2FEET * 60,
                        //    (last.Time - first.Time).TotalSeconds), true);
                    }

                    firstPoint = lastPoint;
                }
            }
            break;
            }

            if (Infringements.Count == 0)
            {
                AddNote("not infringed");
            }
        }