/// <summary>
        /// Carry forward the cd parameter value to future event & timing activities to know which page they occurred on.
        /// </summary>
        /// <param name="activity">Current activity being processed.</param>
        /// <param name="parameters">Current parameters for this request.</param>
        private void CarryForwardParameters(MeasurementActivity activity, ICollection<KeyValuePair<string, string>> parameters)
        {
            if ((activity is EventActivity || activity is TimedEventActivity) && lastCdParameterValue != null)
                parameters.Add(KeyValuePair.Create("cd", lastCdParameterValue));

            if (parameters.Any(k => k.Key == "cd"))
                lastCdParameterValue = parameters.First(p => p.Key == "cd").Value;
        }
        /// <summary>
        /// Preserves the last transaction seen and carries it forward to subsequent
        /// transaction items.
        /// </summary>
        /// <param name="activity">Current activity being tracked.</param>
        private void CarryForwardLastTransaction(MeasurementActivity activity)
        {
            if (activity is TransactionActivity)
                lastTransaction = (TransactionActivity) activity;

            if (activity is TransactionItemActivity)
                ((TransactionItemActivity) activity).Transaction = lastTransaction;
        }
 /// <summary>
 /// Build a list of the parameters required based on configuration, environment, activity, session, custom variables and state.
 /// </summary>
 /// <param name="activity">MeasurementActivity to build the parameter list for.</param>
 /// <returns>Enumeration of key/value pairs containing the parameters necessary for this request.</returns>
 private ICollection<KeyValuePair<string, string>> BuildParameterList(MeasurementActivity activity)
 {
     return GetParameters()
         .Concat(GetParameters(environment))
         .Concat(GetParameters(configuration))
         .Concat(GetParameters(sessionManager))
         .Concat(MeasurementActivityParameterBuilder.GetActivityParameters(activity))
         .Concat(GetParameters(activity.CustomDimensions))
         .Concat(GetParameters(activity.CustomMetrics))
         .ToList();
 }
 /// <summary>
 /// Build an Measurement Protocol URI from an entry containing activity and custom variables.
 /// </summary>
 /// <param name="activity">Entry to build the URI for.</param>
 /// <returns>URI that when requested will track this activity.</returns>
 public Uri BuildUri(MeasurementActivity activity)
 {
     var parameters = BuildParameterList(activity);
     CarryForwardParameters(activity, parameters);
     var endpoint = configuration.UseSsl ? secureTrackingEndpoint : trackingEndpoint;
     var uriBuilder = new UriBuilder(endpoint) {
         Query = CreateQueryString(parameters),
         Fragment = DateTime.UtcNow.ToString("O")
     };
     return uriBuilder.Uri;
 }
        /// <summary>
        /// Track an activity in analytics.
        /// </summary>
        /// <param name="activity">MeasurementActivity to track in analytics.</param>
        public void Track(MeasurementActivity activity)
        {
            if (sessionManager.VisitorStatus != VisitorStatus.Active) return;

            CarryForwardLastTransaction(activity);

            sessionManager.Hit();
            if (activity.EndSession)
                sessionManager.End();

            var trackingUri = uriBuilder.BuildUri(activity);
            sender(trackingUri);
        }
        /// <summary>
        /// Build an Measurement Protocol URI from an entry containing activity and custom variables.
        /// </summary>
        /// <param name="activity">Entry to build the URI for.</param>
        /// <returns>URI that when requested will track this activity.</returns>
        public Uri BuildUri(MeasurementActivity activity)
        {
            var parameters = BuildParameterList(activity);
            CarryForwardParameters(activity, parameters);
            var endpoint = configuration.UseSsl ? secureTrackingEndpoint : trackingEndpoint;

            // Fragment is only added temporarily and used to calculate queue time.
            // It will be removed in MeasurementAnalyticsClient.AdjustUriBeforeRequest.
            var uriBuilder = new UriBuilder(endpoint) {
                Query = CreateQueryString(parameters),
                Fragment = DateTime.UtcNow.ToString("O")
            };
            return uriBuilder.Uri;
        }
        /// <summary>
        /// Transfer any pending custom dimensions and metrics to the activity that the
        /// activity doesn't already have a value for so they can be sent.
        /// </summary>
        /// <param name="activity">Activity to merge custom dimensions and metrics into.</param>
        private void TransferCustomsToActivity(MeasurementActivity activity)
        {
            for (var i = 0; i < customDimensions.Length; i++)
                if (activity.CustomDimensions[i] == null && customDimensions[i] != null)
                {
                    activity.CustomDimensions[i] = customDimensions[i];
                    customDimensions[i] = null;
                }

            for (var i = 0; i < customMetrics.Length; i++)
                if (activity.CustomMetrics[i] == null && customMetrics[i] != null)
                {
                    activity.CustomMetrics[i] = customMetrics[i];
                    customMetrics[i] = null;
                }
        }
        /// <summary>
        /// Track a activity in analytics.
        /// </summary>
        /// <param name="activity">Activity to track in analytics.</param>
        /// <param name="endSession">True if this event should end the session.</param>
        public void Track(MeasurementActivity activity, bool endSession = false)
        {
            if (activity is AutoTimedEventActivity)
                ((AutoTimedEventActivity)activity).End();

            OnTrack(this, activity);

            activity.EndSession = activity.EndSession | endSession;

            TransferCustomsToActivity(activity);

            if (tracker == null)
                queue.Enqueue(activity);
            else
                tracker.Track(activity);
        }
        /// <summary>
        /// Turn an IMeasurementActivity into the key/value pairs necessary for building
        /// the URI to track with Measurement Protocol.
        /// </summary>
        /// <param name="activity">Activity to turn into key/value pairs.</param>
        /// <returns>Enumerable of key/value pairs representing the activity.</returns>
        internal static IEnumerable<KeyValuePair<string, string>> GetActivityParameters(MeasurementActivity activity)
        {
            if (activity is ScreenViewActivity)
                return GetParameters((ScreenViewActivity)activity);
            if (activity is ContentViewActivity)
                return GetParameters((ContentViewActivity)activity);
            if (activity is ExceptionActivity)
                return GetParameters((ExceptionActivity)activity);
            if (activity is EventActivity)
                return GetParameters((EventActivity)activity);
            if (activity is TimedEventActivity)
                return GetParameters((TimedEventActivity)activity);
            if (activity is SocialActivity)
                return GetParameters((SocialActivity)activity);
            if (activity is TransactionActivity)
                return GetParameters((TransactionActivity)activity);
            if (activity is TransactionItemActivity)
                return GetParameters((TransactionItemActivity)activity);

            Debug.Assert(false, "Unknown Activity type");
            return Enumerable.Empty<KeyValuePair<string, string>>();
        }
 /// <summary>
 /// Obtain the key/value pairs for a MeasurementActivity base class.
 /// </summary>
 /// <param name="activity">MeasurementActivity to turn into key/value pairs.</param>
 /// <returns>Key/value pairs representing this MeasurementActivity.</returns>
 internal static IEnumerable<KeyValuePair<string, string>> GetCommonParameters(MeasurementActivity activity)
 {
     if (activity.NonInteraction)
         yield return KeyValuePair.Create("ni", "1");            
 }