public static int? SendTraining(DateTime startDate, bool hasStartTime, TimeSpan duration, float?TE, int? cadenceAvg, float? distance, string comment,
            int? hrAvg, int? hrMax, int? intensity, int? kcal, string privateComment, int? repetitions, int? sets,
            int trainingType, TrainingInterval[] laps, TrackPoint[] trackPoints, Privacy RoutePrivacy, string[] equipment)
        {
            if (Settings.Settings.Instance.User.Username.Length == 0 || Settings.Settings.Instance.User.Password.Length == 0)
            {
                MessageBox.Show("Funbeat user name and password must be entered in the ST2funbeat settings", "Login failure", MessageBoxButtons.OK);
                return null;
            }
            else if (Settings.Settings.Instance.User.LoginId.Length == 0 || Settings.Settings.Instance.User.LoginSecret.Length == 0)
            {
                MessageBox.Show("Funbeat user name and password could not be confirmed. Make sure they are correct and that you are connected to the internet", "Login failure", MessageBoxButtons.OK);
                return null;
            }
            else
            {
                Training training = new Training();
                training.Description = comment;
                training.Distance = distance;
                training.StartDateTime = startDate;
                training.HasTimeOfDay = hasStartTime;
                training.Duration = new Duration();
                training.Duration.Hours = duration.Hours;
                training.Duration.Minutes = duration.Minutes;
                training.Duration.Seconds = duration.Seconds;

                training.TE = TE;
                training.CadAvg = cadenceAvg;
                training.HRAvg = hrAvg;
                training.HRMax = hrMax;
                training.Intensity = intensity;
                training.KCal = kcal;
                training.PrivateComment = privateComment;
                training.Repetitions = repetitions;
                training.Sets = sets;
                training.TrainingTypeID = trainingType;
                training.IntervalsAndLaps = laps;
                training.NewRoutePrivacy = RoutePrivacy;
                training.Equipment = equipment;

                // Workaround for the funbeat server not working when two or more subsequent GPS points have identical position
                // Ought to be fixed on the server side
                if (trackPoints != null)
                {
                    List<TrackPoint> trackPointsList = new List<TrackPoint>(trackPoints);
                    if (trackPointsList.Count > 0)
                    {
                        for (int i = trackPointsList.Count - 1; i > 0; i--)
                        {
                            TrackPoint tp = trackPointsList[i];
                            if (tp.Latitude == trackPointsList[i - 1].Latitude && tp.Longitude == trackPointsList[i - 1].Longitude)
                            {
                                tp.Latitude = null;
                                tp.Longitude = null;
                            }
                            if (!tp.Altitude.HasValue && !tp.Cad.HasValue && !tp.Distance.HasValue && !tp.HR.HasValue &&
                                !tp.Latitude.HasValue && !tp.Longitude.HasValue && !tp.Pace.HasValue && !tp.Power.HasValue &&
                                !tp.Speed.HasValue)
                                trackPointsList.Remove(tp);
                        }

                    }
                    training.TrackPoints = trackPointsList.ToArray();
                }
                else
                {
                    training.TrackPoints = trackPoints; // i.e. null
                }

                if (training.TrackPoints != null)
                {
                    training.NewRouteName = startDate.ToString();
                }
                else
                {
                    // Do nothing, leave at value null
                }

            #if DEBUG
                FileInfo t = new FileInfo("Training.txt");
                StreamWriter Tex = t.CreateText();
                Tex.WriteLine("Description: " + comment.ToString());
                Tex.WriteLine("Distance: " + distance.ToString());
                Tex.WriteLine("StartDateTime: " + startDate.ToString());
                Tex.WriteLine("HasTimeOfDay: " + hasStartTime.ToString());
                Tex.WriteLine("Duration: " + duration.ToString());
                Tex.WriteLine("TE: " + TE.ToString());
                Tex.WriteLine("CadAvg: " + cadenceAvg.ToString());
                Tex.WriteLine("HRAvg: " + hrAvg.ToString());
                Tex.WriteLine("HRMax: " + hrMax.ToString());
                Tex.WriteLine("Intensity: " + intensity.ToString());
                Tex.WriteLine("KCal: " + kcal.ToString());
                Tex.WriteLine("PrivateComment: " + privateComment.ToString());
                Tex.WriteLine("Repetitions: " + repetitions.ToString());
                Tex.WriteLine("Sets: " + sets.ToString());
                Tex.WriteLine("TrainingTypeID: " + trainingType.ToString());
                Tex.WriteLine("NewRouteName: " + startDate.ToString());
                Tex.WriteLine("NewRoutePrivacy: " + training.NewRoutePrivacy.ToString());
                Tex.WriteLine();
                if (trackPoints != null)
                {
                    for(int i=0; i<trackPoints.Length; i++)
                    {
                        TrackPoint tp = trackPoints[i];
                        Tex.WriteLine("Trackpoint");
                        Tex.WriteLine("isStartPoint: " + tp.isStartPoint.ToString());
                        Tex.WriteLine("TimeStamp: " + tp.TimeStamp.ToString());
                        Tex.WriteLine("HR: " + tp.HR.ToString());
                        Tex.WriteLine("Latitude: " + tp.Latitude.ToString());
                        Tex.WriteLine("Longitude: " + tp.Longitude.ToString());
                        Tex.WriteLine("Pace: " + tp.Pace.ToString());
                        Tex.WriteLine("Power: " + tp.Power.ToString());
                        Tex.WriteLine("Speed: " + tp.Speed.ToString());
                        Tex.WriteLine("Altitude: " + tp.Altitude.ToString());
                        Tex.WriteLine("Cadence: " + tp.Cad.ToString());
                        Tex.WriteLine("Distance: " + tp.Distance.ToString());
                        Tex.WriteLine("TrackPoint no;" + i.ToString());

                        if (i>0)
                        {
                            if (tp.Latitude == trackPoints[i - 1].Latitude && tp.Longitude == trackPoints[i - 1].Longitude)
                            {
                                Tex.WriteLine("Marker: Same latitude and longitude");
                            }
                            else
                            {
                                if (tp.Latitude == trackPoints[i - 1].Latitude)
                                    Tex.WriteLine("Marker: Same latitude");
                                if (tp.Longitude == trackPoints[i - 1].Longitude)
                                    Tex.WriteLine("Marker: Same longitude");
                            }
                            if (tp.TimeStamp.CompareTo(trackPoints[i - 1].TimeStamp) == 0)
                                Tex.WriteLine("Marker: Same time");
                        }

                        Tex.WriteLine();
                    }
                }

                if(equipment != null)
                {
                    for (int i = 0; i < equipment.Length; i++)
                    {
                        Tex.WriteLine("Equipment: " + equipment[i]);
                    }
                }
                Tex.Close();
            #endif

                try
                {
                    MobileService client = new MobileService();
                    ClientServerRelationResult result = client.SaveTraining(appID,
                                                                            Settings.Settings.Instance.User.LoginId,
                                                                            Settings.Settings.Instance.User.LoginSecret,
                                                                            training,
                                                                            clientID);
                    if (result != null)
                        return result.ServerID;
                    else
                        return null;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(string.Concat("Problem sending the activity to funbeat\n", ex.Message), "Funbeat communication error");
                    return null;
                }
            }
        }
        // Use any data track as a base when creating the TrackPoints.
        // Set GPS properties to null
        private TrackPoint[] GetTrackPointsFromDataTrack(IActivity activity)
        {
            ITimeDataSeries<float> ReferenceTrack;
            if (activity.DistanceMetersTrack != null)
                ReferenceTrack = new TimeDataSeries<float>(activity.DistanceMetersTrack);
            else if (activity.HeartRatePerMinuteTrack != null)
                ReferenceTrack = new TimeDataSeries<float>(activity.HeartRatePerMinuteTrack);
            else if (activity.ElevationMetersTrack != null)
                ReferenceTrack = new TimeDataSeries<float>(activity.ElevationMetersTrack);
            else if (activity.CadencePerMinuteTrack != null)
                ReferenceTrack = new TimeDataSeries<float>(activity.CadencePerMinuteTrack);
            else
                return null;
            ReferenceTrack.AllowMultipleAtSameTime = false;

            List<TrackPoint> tps = new List<TrackPoint>();
            foreach (ITimeValueEntry<float> p in ReferenceTrack)
            {

                TrackPoint tp = new TrackPoint();
                if (p == ReferenceTrack[0])
                    tp.isStartPoint = true;

                tp.TimeStamp = ConvertToLocalTime(ReferenceTrack.StartTime.AddSeconds(p.ElapsedSeconds));

                DateTime actualTime = ReferenceTrack.StartTime.AddSeconds(p.ElapsedSeconds);

                // Get distance
                if (activity.DistanceMetersTrack != null)
                {
                    if (actualTime < activity.DistanceMetersTrack.StartTime)
                        actualTime = activity.DistanceMetersTrack.StartTime;

                    ITimeValueEntry<float> interpolatedDistance = activity.DistanceMetersTrack.GetInterpolatedValue(actualTime);
                    if (interpolatedDistance != null)
                    {
                        tp.Distance = interpolatedDistance.Value / 1000;
                        if (double.IsNaN((double)tp.Distance))
                        {
                            tp.Distance = null;
                        }
                    }
                    else
                    {
                        tp.Distance = null;
                    }
                }
                else
                {
                    tp.Distance = null;
                }

                // Get elevation track
                if (activity.ElevationMetersTrack != null)
                {
                    if (actualTime < activity.ElevationMetersTrack.StartTime)
                        actualTime = activity.ElevationMetersTrack.StartTime;

                    ITimeValueEntry<float> interpolatedElevation = activity.ElevationMetersTrack.GetInterpolatedValue(actualTime);
                    if (interpolatedElevation != null)
                    {
                        tp.Altitude = interpolatedElevation.Value;
                        if (double.IsNaN((double)tp.Altitude))
                        {
                            tp.Altitude = null;
                        }

                    }
                    else
                    {
                        tp.Altitude = null;
                    }
                }
                else
                {
                    tp.Altitude = null;
                }

                // Get heart rate track
                if (activity.HeartRatePerMinuteTrack != null)
                {
                    if (actualTime < activity.HeartRatePerMinuteTrack.StartTime)
                        actualTime = activity.HeartRatePerMinuteTrack.StartTime;

                    ITimeValueEntry<float> interpolatedHR = activity.HeartRatePerMinuteTrack.GetInterpolatedValue(actualTime);
                    if (interpolatedHR != null)
                    {
                        float heartRate = interpolatedHR.Value;
                        tp.HR = Convert.ToInt32(heartRate);
                        if (double.IsNaN((double)tp.HR))
                        {
                            tp.HR = null;
                        }
                    }
                    else
                    {
                        tp.HR = null;
                    }
                }
                else
                {
                    tp.HR = null;
                }

                // Get cadence track
                if (activity.CadencePerMinuteTrack != null)
                {
                    if (actualTime < activity.CadencePerMinuteTrack.StartTime)
                        actualTime = activity.CadencePerMinuteTrack.StartTime;

                    ITimeValueEntry<float> interpolatedCadence = activity.CadencePerMinuteTrack.GetInterpolatedValue(actualTime);
                    if (interpolatedCadence != null)
                    {
                        float cadence = interpolatedCadence.Value;
                        tp.Cad = Convert.ToInt32(cadence);
                        if (double.IsNaN((double)tp.Cad))
                        {
                            tp.Cad = null;
                        }
                    }
                    else
                    {
                        tp.Cad = null;
                    }
                }
                else
                {
                    tp.Cad = null;
                }

                tp.Latitude = null;
                tp.Longitude = null;

                tps.Add(tp);
            }
            return tps.ToArray();
        }
        // Use the GPS track as a base when creating the TrackPoints.
        // Use heartbeat data if available
        private TrackPoint[] GetTrackPointsFromGPSTrack(IActivity activity)
        {
            if (activity.GPSRoute == null)
                return null;
            List<TrackPoint> tps = new List<TrackPoint>();
            double accDistance = 0;
            ITimeValueEntry<IGPSPoint> prevPoint = null;

            // Removing GPS points at identical time is a workaround for the funbeat server not being able to handle them
            // Ought to be fixed on the server side
            IGPSRoute ActGPSRoute = new GPSRoute(activity.GPSRoute);
            ActGPSRoute.AllowMultipleAtSameTime = false;

            foreach (ITimeValueEntry<IGPSPoint> p in ActGPSRoute)
            {

                TrackPoint tp = new TrackPoint();
                if (p == ActGPSRoute[0])
                    tp.isStartPoint = true;
                tp.TimeStamp = ConvertToLocalTime(ActGPSRoute.StartTime.AddSeconds(p.ElapsedSeconds));

                DateTime actualTime = ActGPSRoute.StartTime.AddSeconds(p.ElapsedSeconds);

                // Get heartrate track
                if (activity.HeartRatePerMinuteTrack != null)
                {
                    if (actualTime < activity.HeartRatePerMinuteTrack.StartTime)
                        actualTime = activity.HeartRatePerMinuteTrack.StartTime;

                    ITimeValueEntry<float> interpolatedHR = activity.HeartRatePerMinuteTrack.GetInterpolatedValue(actualTime);
                    if (interpolatedHR != null)
                    {
                        float heartRate = interpolatedHR.Value;
                        tp.HR = Convert.ToInt32(heartRate);
                    }
                    else
                    {
                        tp.HR = null;
                    }
                }
                else
                {
                    tp.HR = null;
                }

                // Get cadence track
                if (activity.CadencePerMinuteTrack != null)
                {
                    if (actualTime < activity.CadencePerMinuteTrack.StartTime)
                        actualTime = activity.CadencePerMinuteTrack.StartTime;

                    ITimeValueEntry<float> interpolatedCadence = activity.CadencePerMinuteTrack.GetInterpolatedValue(actualTime);
                    if (interpolatedCadence != null)
                    {
                        float cadence = interpolatedCadence.Value;
                        tp.Cad = Convert.ToInt32(cadence);
                        if (double.IsNaN((double)tp.Cad))
                        {
                            tp.Cad = null;
                        }
                    }
                    else
                    {
                        tp.Cad = null;
                    }
                }
                else
                {
                    tp.Cad = null;
                }

                tp.Latitude = Convert.ToDouble(p.Value.LatitudeDegrees);
                tp.Longitude = Convert.ToDouble(p.Value.LongitudeDegrees);
                tp.Altitude = Convert.ToDouble(p.Value.ElevationMeters);
                if (double.IsNaN((double)tp.Altitude))
                {
                    tp.Altitude = null;
                }

                if (prevPoint != null)
                    accDistance += p.Value.DistanceMetersToPoint(prevPoint.Value);
                tp.Distance = accDistance / 1000;

                tps.Add(tp);
                prevPoint = p;
            }
            return tps.ToArray();
        }
        public void GetExportData(IActivity activity,
            bool boExportNameInComment,
            out DateTime startDate,
            out bool hasStartTime,
            out TimeSpan duration,
            out float? TE,
            out int? cadenceAvg,
            out float? distance,
            out string comment,
            out int? hrAvg,
            out int? hrMax,
            out int? intensity,
            out int? kcal,
            out string privateComment,
            out int? repetitions,
            out int? sets,
            out TrainingInterval[] laps,
            out TrackPoint[] trackPoints)
        {
            if (activity != null)
            {

                int? RPECustFieldData, RepetitionsCustFieldData, SetsCustFieldData;
                double? TECustFieldData;
            #if !ST_2_1
                GetCustomFieldsData(activity,
                                    out RPECustFieldData,
                                    out TECustFieldData,
                                    out RepetitionsCustFieldData,
                                    out SetsCustFieldData);
            #else
                RPECustFieldData = null;
                TECustFieldData = null;
                RepetitionsCustFieldData = null;
                SetsCustFieldData = null;
            #endif

                // Get pulse data. Works with and without a HR data track.
                ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(activity);
                if (activityInfo.AverageHeartRate == 0)
                    hrAvg = null;
                else
                    hrAvg = (int?)Math.Round(activityInfo.AverageHeartRate);
                if (activityInfo.MaximumHeartRate == 0)
                    hrMax = null;
                else
                    hrMax = (int?)Math.Round(activityInfo.MaximumHeartRate);
                if (activityInfo.AverageCadence == 0)
                    cadenceAvg = null;
                else
                    cadenceAvg = (int?)Math.Round(activityInfo.AverageCadence);

                startDate = ConvertToLocalTime(activity.StartTime);
                hasStartTime = activity.HasStartTime;
                duration = activityInfo.Time;
                TE = (float?)TECustFieldData;
                distance = (float?)activityInfo.DistanceMeters/1000;
                if (boExportNameInComment)
                {
                    comment = activity.Name + "\r\n" + activity.Notes;
                }
                else
                {
                    comment = activity.Notes;
                }
                intensity = RPECustFieldData;
                if (activity.TotalCalories == 0)
                    kcal = null;
                else
                    kcal = (int?)Math.Round(activity.TotalCalories);
                privateComment = "SportsTracks reference: " + activity.ReferenceId;
                repetitions = RepetitionsCustFieldData;
                sets = SetsCustFieldData;
                laps = GetLaps(activity);
                trackPoints = GetTrackPoints(activity);
            }
            else
            {
                startDate = new DateTime(1900, 1, 1, 0, 0, 0);
                hasStartTime = false;
                duration = new TimeSpan(0, 0, 0);
                TE = null;
                cadenceAvg = null;
                distance = null;
                comment = "";
                hrAvg = null;
                hrMax = null;
                intensity = null;
                kcal = null;
                privateComment = "";
                repetitions = null;
                sets = null;
                laps = null;
                trackPoints = null;

            }
        }