public void TestFITScheduleSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField messageField;
            DateTime serializedDate;
            DateTime referenceDate = new DateTime(1989, 12, 31);

            // Scheduled dates
            placeholderWorkout.Name = "WorkoutTest5";
            serializedDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 12, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual((UInt32)(serializedDate - referenceDate).TotalSeconds,
                            messageField.GetUInt32(),
                            "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest6";
            serializedDate.AddDays(1);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual((UInt32)(serializedDate - referenceDate).TotalSeconds,
                            messageField.GetUInt32(),
                            "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            // Test specific date with hardcoded result
            placeholderWorkout.Name = "WorkoutTest6b";
            serializedDate = new DateTime(1999, 12, 31, 12, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual(315576000, messageField.GetUInt32(), "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            // New Zealand time (UST+12H) is a case where we had problems because the offset changes the day
            String currentZoneName = Time.CurrentTimeZone.standardName;
            placeholderWorkout.Name = "WorkoutTest7";
            bool res = Time.SetTimeZone("New Zealand Standard Time");
            serializedDate = new DateTime(1999, 12, 31, 0, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            res = Time.SetTimeZone(currentZoneName);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual(315576000, messageField.GetUInt32(), "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField messageField;

            // Name
            placeholderWorkout.Name = "WorkoutTest1";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);
            Assert.IsNotNull(messageField, "Workout name field not serialized for step");
            Assert.AreEqual("WorkoutTest1", messageField.GetString(), "Invalid workout name FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest2";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);
            Assert.IsNotNull(messageField, "Workout name field not serialized for step");
            Assert.AreEqual("WorkoutTest2", messageField.GetString(), "Invalid workout name FIT serialization");
            serializedMessage.Clear();

            // Category/Sport
            placeholderWorkout.Name = "WorkoutTest3";
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[5];
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.SportType);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(FITSports.Cycling, (FITSports)messageField.GetEnum(), "Invalid workout sport FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest4";
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[6];
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.SportType);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(FITSports.Running, (FITSports)messageField.GetEnum(), "Invalid workout sport FIT serialization");
            serializedMessage.Clear();

            // Number of steps
            placeholderWorkout.Name = "WorkoutTest5";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid workout step count FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest6";
            placeholderWorkout.Steps.AddStepToRoot(new RepeatStep(placeholderWorkout));
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(3, messageField.GetUInt16(), "Invalid workout step count FIT serialization");
            serializedMessage.Clear();

            // Do not test steps serialization here, it has it's own test
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Repeat count
            RepeatCountDuration repeatDuration = new RepeatCountDuration(placeholderStep);
            repeatDuration.RepetitionCount = 3;
            repeatDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat count duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatCount, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat count duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat count duration");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid target value in field for repeat count duration");
            serializedMessage.Clear();

            repeatDuration.RepetitionCount = 7;
            repeatDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat count duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatCount, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat count duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat count duration");
            Assert.AreEqual(7, messageField.GetUInt32(), "Invalid target value in field for repeat count duration");
            serializedMessage.Clear();

            // Repeat until calories
            RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
            caloriesDuration.CaloriesToSpend = 550;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until calories duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilCalories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until calories duration");
            Assert.AreEqual(550, messageField.GetUInt32(), "Invalid target value in field for repeat until calories duration");
            serializedMessage.Clear();

            caloriesDuration.CaloriesToSpend = 750;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until calories duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilCalories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until calories duration");
            Assert.AreEqual(750, messageField.GetUInt32(), "Invalid target value in field for repeat until calories duration");
            serializedMessage.Clear();

            // Repeat until distance
            RepeatUntilDistanceDuration distanceDuration = new RepeatUntilDistanceDuration(1, Length.Units.Kilometer, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until distance duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilDistance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until distance duration");
            Assert.AreEqual(100000, messageField.GetUInt32(), "Invalid target value in field for repeat until distance duration");
            serializedMessage.Clear();

            distanceDuration = new RepeatUntilDistanceDuration(1, Length.Units.Mile, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until distance duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilDistance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until distance duration");
            Assert.AreEqual(160934, messageField.GetUInt32(), "Invalid target value in field for repeat until distance duration");
            serializedMessage.Clear();

            // Repeat until HRAbove
            RepeatUntilHeartRateAboveDuration hrAboveDuration = new RepeatUntilHeartRateAboveDuration(placeholderStep);
            hrAboveDuration.IsPercentageMaxHeartRate = false;
            hrAboveDuration.MaxHeartRate = 160;
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(260, messageField.GetUInt32(), "Invalid target value in field for repeat until HRAbove duration");
            serializedMessage.Clear();

            hrAboveDuration.IsPercentageMaxHeartRate = true;
            hrAboveDuration.MaxHeartRate = 80;
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid target value in field for repeat until HRAbove duration");
            serializedMessage.Clear();

            // Repeat until HRBelow
            RepeatUntilHeartRateBelowDuration hrBelowDuration = new RepeatUntilHeartRateBelowDuration(placeholderStep);
            hrBelowDuration.IsPercentageMaxHeartRate = false;
            hrBelowDuration.MinHeartRate = 150;
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(250, messageField.GetUInt32(), "Invalid target value in field for repeat until HRBelow duration");
            serializedMessage.Clear();

            hrBelowDuration.IsPercentageMaxHeartRate = true;
            hrBelowDuration.MinHeartRate = 70;
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid target value in field for repeat until HRBelow duration");
            serializedMessage.Clear();

            // Repeat until PowerAbove
            RepeatUntilPowerAboveDuration powerAboveDuration = new RepeatUntilPowerAboveDuration(placeholderStep);
            powerAboveDuration.IsPercentFTP = false;
            powerAboveDuration.MaxPower = 250;
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(1250, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerAbove duration");
            serializedMessage.Clear();

            powerAboveDuration.IsPercentFTP = true;
            powerAboveDuration.MaxPower = 100;
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(100, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerAbove duration");
            serializedMessage.Clear();

            // Repeat until HRBelow
            RepeatUntilPowerBelowDuration powerBelowDuration = new RepeatUntilPowerBelowDuration(placeholderStep);
            powerBelowDuration.IsPercentFTP = false;
            powerBelowDuration.MinPower = 150;
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerBelow duration");
            serializedMessage.Clear();

            powerBelowDuration.IsPercentFTP = true;
            powerBelowDuration.MinPower = 270;
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(270, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerBelow duration");
            serializedMessage.Clear();

            // Repeat until time
            RepeatUntilTimeDuration timeDuration = new RepeatUntilTimeDuration(placeholderStep);
            timeDuration.TimeInSeconds = 600;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilTime, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until time duration");
            Assert.AreEqual(600000, messageField.GetUInt32(), "Invalid target value in field for repeat until time duration");
            serializedMessage.Clear();

            timeDuration.TimeInSeconds = 350;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilTime, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until time duration");
            Assert.AreEqual(350000, messageField.GetUInt32(), "Invalid target value in field for repeat until time duration");
            serializedMessage.Clear();
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // This is required to determine the step's id in the workout during serialization
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);

            // No target
            NullTarget noTarget = new NullTarget(placeholderStep);
            noTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid no target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.NoTarget, messageField.GetEnum(), "Invalid target type in field for target");
            serializedMessage.Clear();

            // Cadence targets
            BaseCadenceTarget cadenceTarget = new BaseCadenceTarget(placeholderStep);

            // Cadence range
            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(80, 90, cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(90, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(60, 120, cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(60, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            // Cadence ST zone
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(85, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[4], cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(100, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            // Speed targets
            BaseSpeedTarget speedTarget = new BaseSpeedTarget(placeholderStep);

            // Speed range
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Mile, Speed.Units.Speed, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8940, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(13411, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            // Pace shouldn't change the values saved
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(8940, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(13411, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange pace target");
            serializedMessage.Clear();

            // Speed Garmin zone
            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(1, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for SpeedGTC target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(3, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for SpeedGTC target");
            serializedMessage.Clear();

            // Speed ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(2777, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            // Pace shouldn't change the values saved
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange pace target");
            serializedMessage.Clear();

            // Heart rate targets
            BaseHeartRateTarget hrTarget = new BaseHeartRateTarget(placeholderStep);

            // HR range
            hrTarget.ConcreteTarget = new HeartRateRangeTarget(130, 170, false, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(230, messageField.GetUInt32(), "Invalid lower value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(270, messageField.GetUInt32(), "Invalid upper value in field for HRRange target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(100, 190, false, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(200, messageField.GetUInt32(), "Invalid lower value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(290, messageField.GetUInt32(), "Invalid upper value in field for HRRange target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(50, 70, true, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(50, messageField.GetUInt32(), "Invalid lower value in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid upper value in field for HRRange %Max target");
            serializedMessage.Clear();

            // HR Garmin zone
            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(1, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for HRGTC target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(3, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for HRGTC target");
            serializedMessage.Clear();

            // HR ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(240, messageField.GetUInt32(), "Invalid lower value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(260, messageField.GetUInt32(), "Invalid upper value in field for HRST target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[4], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(280, messageField.GetUInt32(), "Invalid lower value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(340, messageField.GetUInt32(), "Invalid upper value in field for HRST target");
            serializedMessage.Clear();

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = true;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(68, messageField.GetUInt32(), "Invalid lower value in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(82, messageField.GetUInt32(), "Invalid upper value in field for HRST %Max target");
            serializedMessage.Clear();

            // Power targets
            BasePowerTarget powerTarget = new BasePowerTarget(placeholderStep);

            // Power range
            powerTarget.ConcreteTarget = new PowerRangeTarget(150, 200, false, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid lower value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1200, messageField.GetUInt32(), "Invalid upper value in field for PowerRange target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerRangeTarget(300, 400, false, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1300, messageField.GetUInt32(), "Invalid lower value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1400, messageField.GetUInt32(), "Invalid upper value in field for PowerRange target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerRangeTarget(67, 80, true, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(67, messageField.GetUInt32(), "Invalid lower value in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid upper value in field for PowerRange %FTP target");
            serializedMessage.Clear();

            // Power Garmin zone
            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(1, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for PowerGTC target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(3, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for PowerGTC target");
            serializedMessage.Clear();

            // Power ST zone
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[4];
            Options.Instance.ExportSportTracksPowerAsPercentFTP = false;
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[1], powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid lower value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerSTFTP target FIT serialization");
            Assert.AreEqual(1200, messageField.GetUInt32(), "Invalid upper value in field for PowerST target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(1300, messageField.GetUInt32(), "Invalid lower value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerSTFTP target FIT serialization");
            Assert.AreEqual(1400, messageField.GetUInt32(), "Invalid upper value in field for PowerST target");
            serializedMessage.Clear();

            Options.Instance.ExportSportTracksPowerAsPercentFTP = true;
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid lower value in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(160, messageField.GetUInt32(), "Invalid upper value in field for PowerST %FTP target");
            serializedMessage.Clear();

            // Make sure to reset options to previous values
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = exportHRAsMax;
            Options.Instance.ExportSportTracksPowerAsPercentFTP = exportPowerAsFTP;
        }
        public void TestRepeatStepFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            placeholderWorkout.Steps.RemoveStep(placeholderWorkout.Steps[0]);

            // Single child
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Multiple children
            // - Root
            //  - Repeat step (id = 2)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            repeatStep.StepsToRepeat.Add(new RegularStep(placeholderWorkout));
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Nested repeat steps
            //  - Repeat step (id = 4)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            //   - Repeat step (id = 3)
            //    - Regular step (id = 2)
            repeatStep.StepsToRepeat.Add(new RepeatStep(placeholderWorkout));
            repeatStep.StepsToRepeat[2].FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(2, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Nested repeats with 2nd repeat as first nested step
            // - Root
            //  - Repeat step (id = 4)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            //   - Repeat step (id = 3)
            //    - Regular step (id = 2)
            //  - Repeat step (id = 8)
            //   - Repeat step (id = 6)
            //    - Regular step (id = 5)
            //   - Regular step (id = 7)
            repeatStep = new RepeatStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            repeatStep.StepsToRepeat.Insert(0, new RepeatStep(placeholderWorkout));
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(5, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();
        }
        public void TestRegularStepFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = placeholderWorkout.Steps[0] as RegularStep;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Active step
            placeholderStep.Name = "TestStep1";
            placeholderStep.Intensity = RegularStep.StepIntensity.Active;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(0, messageField.GetUInt16(), "Invalid message index FIT serialization for regular step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep1", messageField.GetString(), "Invalid name in field for active step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Active, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for active step");
            serializedMessage.Clear();

            // Rest step
            placeholderStep.Name = "TestStep2";
            placeholderStep.Intensity = RegularStep.StepIntensity.Rest;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(0, messageField.GetUInt16(), "Invalid message index FIT serialization for rest step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep2", messageField.GetString(), "Invalid name in field for rest step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Rest, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for rest step");
            serializedMessage.Clear();

            // Warmup step
            placeholderStep = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);
            placeholderStep.Name = "TestStep3";
            placeholderStep.Intensity = RegularStep.StepIntensity.Warmup;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid message index FIT serialization for warmup step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep3", messageField.GetString(), "Invalid name in field for warmup step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Warmup, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for warmup step");
            serializedMessage.Clear();

            // Cooldown step
            placeholderStep.Name = "TestStep4";
            placeholderStep.Intensity = RegularStep.StepIntensity.Cooldown;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid message index FIT serialization for cooldwon step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep4", messageField.GetString(), "Invalid name in field for cooldown step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Cooldown, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for cooldown step");
            serializedMessage.Clear();
        }
        public virtual void SerializeFITSpeedZones(Stream outputStream)
        {
            UInt16 i = 0;

            foreach (GarminFitnessNamedSpeedZone zone in m_SpeedZones)
            {
                FITMessage zonesTargetMessage = new FITMessage(FITGlobalMessageIds.SpeedZones);
                FITMessageField index = new FITMessageField((Byte)FITSpeedZonesFieldIds.MessageIndex);
                FITMessageField zoneName = new FITMessageField((Byte)FITSpeedZonesFieldIds.Name);
                FITMessageField highSpeed = new FITMessageField((Byte)FITSpeedZonesFieldIds.HighSpeed);

                if (i == 0)
                {
                    index.SetUInt16(i);
                    zoneName.SetString(zone.Name, (Byte)(Constants.MaxNameLength + 1));
                    highSpeed.SetUInt16((UInt16)(zone.Low * 1000));

                    zonesTargetMessage.AddField(index);
                    zonesTargetMessage.AddField(zoneName);
                    zonesTargetMessage.AddField(highSpeed);

                    zonesTargetMessage.Serialize(outputStream);
                    zonesTargetMessage.Clear();
                }

                index.SetUInt16((UInt16)(i + 1));
                zoneName.SetString(zone.Name, (Byte)(Constants.MaxNameLength + 1));
                highSpeed.SetUInt16((UInt16)(zone.High * 1000));

                zonesTargetMessage.AddField(index);
                zonesTargetMessage.AddField(zoneName);
                zonesTargetMessage.AddField(highSpeed);

                zonesTargetMessage.Serialize(outputStream, false);

                ++i;
            }
        }
        public virtual void SerializeFITHRZones(Stream outputStream)
        {
            UInt16 i = 0;

            foreach (GarminFitnessValueRange<GarminFitnessDoubleRange> zone in m_HeartRateZones)
            {
                FITMessage zonesTargetMessage = new FITMessage(FITGlobalMessageIds.HRZones);
                FITMessageField index = new FITMessageField((Byte)FITHRZonesFieldIds.MessageIndex);
                FITMessageField zoneName = new FITMessageField((Byte)FITHRZonesFieldIds.Name);
                FITMessageField highBPM = new FITMessageField((Byte)FITHRZonesFieldIds.HighBPM);

                if (i == 0)
                {
                    index.SetUInt16(i);
                    zoneName.SetString(String.Format("HR Zone {0}", i), (Byte)(Constants.MaxNameLength + 1));
                    highBPM.SetUInt8((Byte)(zone.Lower * MaximumHeartRate));

                    zonesTargetMessage.AddField(index);
                    zonesTargetMessage.AddField(zoneName);
                    zonesTargetMessage.AddField(highBPM);

                    zonesTargetMessage.Serialize(outputStream);
                    zonesTargetMessage.Clear();
                }

                index.SetUInt16((UInt16)(i + 1));
                zoneName.SetString(String.Format("HR Zone {0}", i + 1), (Byte)(Constants.MaxNameLength + 1));
                highBPM.SetUInt8((Byte)(zone.Upper * MaximumHeartRate));

                zonesTargetMessage.AddField(index);
                zonesTargetMessage.AddField(zoneName);
                zonesTargetMessage.AddField(highBPM);

                zonesTargetMessage.Serialize(outputStream, false);

                ++i;
            }
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Lap button
            LapButtonDuration lapDuration = new LapButtonDuration(placeholderStep);
            lapDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for lap button duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Open, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for lap button duration");
            serializedMessage.Clear();

            // Time duration
            TimeDuration timeDuration = new TimeDuration(500, placeholderStep);
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Time, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(500000, messageField.GetUInt32(), "Invalid duration type in field for time duration");
            serializedMessage.Clear();

            timeDuration.TimeInSeconds = 15;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Time, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(15000, messageField.GetUInt32(), "Invalid duration type in field for time duration");
            serializedMessage.Clear();

            // Distance duration
            DistanceDuration distanceDuration = new DistanceDuration(1000, Length.Units.Centimeter, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Distance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for distance duration");
            Assert.AreEqual(1000, messageField.GetUInt32(), "Invalid duration value in field for distance duration");
            serializedMessage.Clear();

            distanceDuration = new DistanceDuration(15, Length.Units.NauticalMile, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Distance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for distance duration");
            Assert.AreEqual(2778000, messageField.GetUInt32(), "Invalid duration value in field for distance duration");
            serializedMessage.Clear();

            // Calories duration
            CaloriesDuration caloriesDuration = new CaloriesDuration(1000, placeholderStep);
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Calories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for calories duration");
            Assert.AreEqual(1000, messageField.GetUInt32(), "Invalid duration value in field for calories duration");
            serializedMessage.Clear();

            caloriesDuration.CaloriesToSpend = 452;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Calories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for calories duration");
            Assert.AreEqual(452, messageField.GetUInt32(), "Invalid duration value in field for calories duration");
            serializedMessage.Clear();

            // HR Above
            HeartRateAboveDuration hrAboveDuration = new HeartRateAboveDuration(145, false, placeholderStep);
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(245, messageField.GetUInt32(), "Invalid duration value in field for HRAbove duration");
            serializedMessage.Clear();

            hrAboveDuration = new HeartRateAboveDuration(75, true, placeholderStep);
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(75, messageField.GetUInt32(), "Invalid duration value in field for HRAbove duration");
            serializedMessage.Clear();

            // HR Below
            HeartRateBelowDuration hrBelowDuration = new HeartRateBelowDuration(145, false, placeholderStep);
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(245, messageField.GetUInt32(), "Invalid duration value in field for HRBelow duration");
            serializedMessage.Clear();

            hrBelowDuration = new HeartRateBelowDuration(55, true, placeholderStep);
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(55, messageField.GetUInt32(), "Invalid duration value in field for HRBelow duration");
            serializedMessage.Clear();

            // Power Above
            PowerAboveDuration powerAboveDuration = new PowerAboveDuration(150, false, placeholderStep);
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid duration value in field for PowerAbove duration");
            serializedMessage.Clear();

            powerAboveDuration = new PowerAboveDuration(150, true, placeholderStep);
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(150, messageField.GetUInt32(), "Invalid duration value in field for PowerAbove duration");
            serializedMessage.Clear();

            // Power Below
            PowerBelowDuration powerBelowDuration = new PowerBelowDuration(175, false, placeholderStep);
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(1175, messageField.GetUInt32(), "Invalid duration value in field for PowerBelow duration");
            serializedMessage.Clear();

            powerBelowDuration = new PowerBelowDuration(60, true, placeholderStep);
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(60, messageField.GetUInt32(), "Invalid duration value in field for PowerBelow duration");
            serializedMessage.Clear();
        }