private static IEnumerable<ICreative> GetSubsequentCreatives(IDocumentCreativeSource creativeSource, Ad creativeConcept, AdDocumentPayload payload)
        {
            if (creativeSource == null) throw new ArgumentNullException("creativeSource");
            if (payload == null) throw new ArgumentNullException("payload");
            if (creativeConcept == null) throw new ArgumentNullException("creativeConcept");

            var adPod = payload.AdPods.FirstOrDefault(ap => ap.Ads.Contains(creativeConcept));
            if (adPod != null)
            {
                return adPod.Ads
                    .SelectMany(a => a.Creatives.OrderBy(c => c.Sequence.GetValueOrDefault(int.MaxValue)))
                    .Where(c => !(c is CreativeCompanions))
                    .SkipWhile(c => c != creativeSource.Creative)
                    .Skip(1);
            }
            return null;
        }
        internal static void MergeWrappedAdBeacons(Ad source, Ad dest)
        {
            foreach (var impression in source.Impressions)
                dest.Impressions.Add(impression);

            foreach (var error in source.Errors)
                dest.Errors.Add(error);
        }
        internal static void MergeWrappedAd(Ad source, Ad dest)
        {
            // carry over impressions
            MergeWrappedAdBeacons(source, dest);

            // carry over tracking events
            foreach (ICreative destCreative in dest.Creatives)
            {
                ICreative sourceCreative = FindMatchingCreative(dest, destCreative, source);
                if (sourceCreative != null)
                {
                    MergeWrappedCreative(sourceCreative, destCreative);
                }
            }
        }
        internal static async Task<AdDocumentPayload> CreateFromVast(Stream stream, int? maxRedirectDepth, bool allowMultipleAds)
#endif
        {
            XDocument xDoc = XDocument.Load(stream);
            XElement vastRoot = xDoc.Element("VAST");
            if (vastRoot == null)
            {
                vastRoot = xDoc.Element("VideoAdServingTemplate");
                if (vastRoot == null) throw new NotImplementedException();
                return await CreateFromVast1(vastRoot, maxRedirectDepth, allowMultipleAds);
            }
            else
            {
                var result = new AdDocumentPayload();
                result.Version = (string)vastRoot.Attribute("version");
                result.Error = (string)vastRoot.Element("Error");

                var eligableAds = vastRoot.Elements("Ad");
                if (!allowMultipleAds)
                {
                    eligableAds = eligableAds.Where(va => string.IsNullOrEmpty((string)va.Attribute("sequence")));
                }
                foreach (var vastAdPod in eligableAds.GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
                {
                    var adPod = new AdPod();
                    foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                    {
                        var ad = new Ad();
                        ad.Id = (string)vastAd.Attribute("id");

                        if (vastAd.Elements("InLine").Any())
                        {
                            var vastAdInline = vastAd.Element("InLine");

                            ad.AdSystem = GetAdSystem(vastAdInline.Element("AdSystem"));

                            ad.Advertiser = (string)vastAdInline.Element("Advertiser");
                            ad.Description = (string)vastAdInline.Element("Description");
                            var error = (string)vastAdInline.Element("Error");
                            if (error != null) ad.Errors.Add(error);
                            ad.Title = (string)vastAdInline.Element("AdTitle");
                            ad.Survey = GetUriValue(vastAdInline.Element("Survey"));

                            ad.Pricing = new Pricing();
                            var pricing = vastAdInline.Element("Pricing");
                            if (pricing != null)
                            {
                                ad.Pricing.Currency = (string)pricing.Attribute("currency");
                                ad.Pricing.Model = (PricingModel)Enum.Parse(typeof(PricingModel), (string)pricing.Attribute("model"), true);
                                ad.Pricing.Value = Convert.ToDouble((string)pricing);
                            }

                            foreach (var vastImpression in vastAdInline.Elements("Impression"))
                            {
                                ad.Impressions.Add((string)vastImpression);
                            }

                            if (vastAdInline.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdInline.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            LoadCreatives(vastAdInline, ad);

                            adPod.Ads.Add(ad);
                        }
                        else if (vastAd.Elements("Wrapper").Any())
                        {
                            Ad wrapper = new Ad();
                            var vastAdWrapper = vastAd.Element("Wrapper");

                            // parse the wrapper itself
                            wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                            var error = (string)vastAdWrapper.Element("Error");
                            if (error != null) wrapper.Errors.Add(error);

                            foreach (var vastImpression in vastAdWrapper.Elements("Impression"))
                            {
                                wrapper.Impressions.Add((string)vastImpression);
                            }

                            LoadCreatives(vastAdWrapper, wrapper);

                            if (vastAdWrapper.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdWrapper.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            AdDocumentPayload wrappedVastDoc = null;
                            var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURI"));
                            if (vastAdUri != null && (!maxRedirectDepth.HasValue || maxRedirectDepth.Value > 0))
                            {
                                try
                                {
                                    // load the stream from the web
                                    using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                                    {
                                        var newAllowMultipleAds = vastAdWrapper.GetBoolAttribute("allowMultipleAds", allowMultipleAds);
                                        var followAdditionalWrappers = vastAdWrapper.GetBoolAttribute("followAdditionalWrappers", true);
                                        int? nextMaxRedirectDepth = followAdditionalWrappers ? (maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth) : 0;
                                        wrappedVastDoc = await CreateFromVast(s, nextMaxRedirectDepth, newAllowMultipleAds);
                                    }
                                }
                                catch { /* swallow */ }
                            }

                            AdPod wrappedAdPod = null;
                            if (wrappedVastDoc != null)
                            {
                                wrappedAdPod = wrappedVastDoc.AdPods.FirstOrDefault();
                            }

                            if (wrappedAdPod == null || !wrappedAdPod.Ads.Any())
                            {
                                // no ads were returned
                                var fallbackOnNoAd = vastAdWrapper.GetBoolAttribute("fallbackOnNoAd", true);
                                if (fallbackOnNoAd)
                                {
                                    wrappedAdPod = FallbackAdPod;
                                }
                            }

                            if (wrappedAdPod != null)
                            {
                                // merge tracking info from this wrapper to every ad in the first adpod in the child
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                    MergeWrappedAd(wrapper, inlineAd);

                                // add each ad from the first adpod in the child to the current adpod
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                    adPod.Ads.Add(inlineAd);
                            }
                        }
                    }
                    result.AdPods.Add(adPod);
                }
                return result;
            }
        }
        private static void LoadCreatives(XElement adElement, Ad ad)
        {
            var vastCreatives = adElement.Element("Creatives");

            if (vastCreatives != null)
            {
                foreach (var vastCreative in vastCreatives.Elements("Creative"))
                {
                    var vastItem = vastCreative.Elements().FirstOrDefault();
                    if (vastItem != null)
                    {
                        ICreative creative = null;
                        switch (vastItem.Name.LocalName)
                        {
                        case "Linear":
                            var vastLinear     = vastItem;
                            var linearCreative = new CreativeLinear();
                            creative = linearCreative;

                            foreach (var trackingEvent in GetTrackingEvents(vastLinear))
                            {
                                linearCreative.TrackingEvents.Add(trackingEvent);
                            }

                            if (vastLinear.Elements("AdParameters").Any())
                            {
                                var xmlEncoded = (bool?)vastLinear.Element("AdParameters").Attribute("xmlEncoded") ?? false;
                                linearCreative.AdParameters = xmlEncoded ? XmlDecode((string)vastLinear.Element("AdParameters")) : (string)vastLinear.Element("AdParameters");
                            }

                            LoadVideoClicks(vastLinear, linearCreative);

                            if (vastLinear.Elements("CreativeExtensions").Any())
                            {
                                foreach (var vastExtension in vastLinear.Element("CreativeExtensions").Elements("CreativeExtension"))
                                {
                                    linearCreative.Extensions.Add(new Extension());     // TODO
                                }
                            }

                            linearCreative.Duration   = ToNullableTimeSpan((string)vastLinear.Element("Duration"));
                            linearCreative.SkipOffset = FlexibleOffset.Parse((string)vastLinear.Attribute("skipoffset"));

                            var vastIcons = vastLinear.Element("Icons");
                            if (vastIcons != null)
                            {
                                foreach (var vastIcon in vastIcons.Elements("Icon"))
                                {
                                    var icon = new Icon();

                                    if (vastIcon.Elements("IconClicks").Any())
                                    {
                                        var iconClicks = vastIcon.Element("IconClicks");

                                        icon.ClickThrough = GetUriValue(iconClicks.Element("IconClickThrough"));

                                        foreach (var clickTracking in iconClicks.Elements("IconClickTracking"))
                                        {
                                            icon.ClickTracking.Add((string)clickTracking);
                                        }
                                    }

                                    icon.ApiFramework = (string)vastIcon.Attribute("apiFramework") ?? string.Empty;
                                    icon.Duration     = ToNullableTimeSpan((string)vastIcon.Attribute("duration")) ?? new TimeSpan?();
                                    icon.Height       = (int?)vastIcon.Attribute("height");
                                    icon.Width        = (int?)vastIcon.Attribute("width");
                                    icon.Offset       = ToNullableTimeSpan((string)vastIcon.Attribute("offset")) ?? new TimeSpan?();
                                    icon.Program      = (string)vastIcon.Attribute("program") ?? string.Empty;
                                    icon.XPosition    = (string)vastIcon.Attribute("xPosition") ?? string.Empty;
                                    icon.YPosition    = (string)vastIcon.Attribute("yPosition") ?? string.Empty;
                                    icon.Item         = GetResources(vastIcon).FirstOrDefault();
                                    foreach (var viewTrackingUrl in vastIcon.Elements("IconViewTracking"))
                                    {
                                        icon.ViewTracking.Add((string)viewTrackingUrl);
                                    }
                                    linearCreative.Icons.Add(icon);
                                }
                            }

                            if (vastLinear.Elements("MediaFiles").Any())
                            {
                                foreach (var vastMedia in vastLinear.Element("MediaFiles").Elements("MediaFile"))
                                {
                                    var mediaFile = new MediaFile();
                                    mediaFile.ApiFramework        = (string)vastMedia.Attribute("apiFramework") ?? string.Empty;
                                    mediaFile.Bitrate             = ToNullableLong((string)vastMedia.Attribute("bitrate"));
                                    mediaFile.Codec               = (string)vastMedia.Attribute("codec") ?? string.Empty;
                                    mediaFile.Delivery            = (MediaFileDelivery)Enum.Parse(typeof(MediaFileDelivery), (string)vastMedia.Attribute("delivery"), true);
                                    mediaFile.Height              = (int?)vastMedia.Attribute("height") ?? 0;
                                    mediaFile.Width               = (int?)vastMedia.Attribute("width") ?? 0;
                                    mediaFile.Id                  = (string)vastMedia.Attribute("id");
                                    mediaFile.MaintainAspectRatio = (bool?)vastMedia.Attribute("maintainAspectRatio") ?? false;
                                    mediaFile.MaxBitrate          = (long?)vastMedia.Attribute("maxBitrate") ?? 0;
                                    mediaFile.MinBitrate          = (long?)vastMedia.Attribute("minBitrate") ?? 0;
                                    mediaFile.Scalable            = (bool?)vastMedia.Attribute("scalable") ?? false;
                                    mediaFile.Type                = (string)vastMedia.Attribute("type");
                                    mediaFile.Value               = GetUriValue(vastMedia);
                                    linearCreative.MediaFiles.Add(mediaFile);
                                }
                            }
                            break;

                        case "CompanionAds":
                            var vastCompanions = vastItem;
                            creative = LoadCompanions(vastCompanions);
                            break;

                        case "NonLinearAds":
                            var vastNonLinears    = vastItem;
                            var nonLinearCreative = new CreativeNonLinears();
                            creative = nonLinearCreative;

                            foreach (var trackingEvent in GetTrackingEvents(vastNonLinears))
                            {
                                nonLinearCreative.TrackingEvents.Add(trackingEvent);
                            }

                            foreach (var vastNonLinear in vastNonLinears.Elements("NonLinear"))
                            {
                                var nonLinear = new NonLinear();

                                if (vastNonLinear.Elements("AdParameters").Any())
                                {
                                    var xmlEncoded = (bool?)vastNonLinear.Element("AdParameters").Attribute("xmlEncoded") ?? false;
                                    nonLinear.AdParameters = xmlEncoded ? XmlDecode((string)vastNonLinear.Element("AdParameters")) : (string)vastNonLinear.Element("AdParameters");
                                }
                                ;
                                nonLinear.ApiFramework = (string)vastNonLinear.Attribute("apiFramework");
                                nonLinear.ClickThrough = GetUriValue(vastNonLinear.Element("NonLinearClickThrough"));
                                foreach (var vastTracking in vastNonLinear.Elements("NonLinearClickTracking"))
                                {
                                    nonLinear.ClickTracking.Add((string)vastTracking);
                                }

                                if (vastNonLinear.Elements("CreativeExtensions").Any())
                                {
                                    foreach (var vastExtension in vastNonLinear.Element("CreativeExtensions").Elements("CreativeExtension"))
                                    {
                                        nonLinear.Extensions.Add(new Extension());     // TODO
                                    }
                                }
                                nonLinear.ExpandedHeight       = (int?)vastNonLinear.Attribute("expandedHeight");
                                nonLinear.ExpandedWidth        = (int?)vastNonLinear.Attribute("expandedWidth");
                                nonLinear.Height               = (int?)vastNonLinear.Attribute("height") ?? 0;
                                nonLinear.Width                = (int?)vastNonLinear.Attribute("width") ?? 0;
                                nonLinear.MaintainAspectRatio  = (bool?)vastNonLinear.Attribute("maintainAspectRatio") ?? false;
                                nonLinear.MinSuggestedDuration = ToNullableTimeSpan((string)vastNonLinear.Attribute("minSuggestedDuration")) ?? new TimeSpan?();
                                nonLinear.Scalable             = (bool?)vastNonLinear.Attribute("scalable") ?? false;
                                nonLinear.Item = GetResources(vastNonLinear).FirstOrDefault();
                                nonLinearCreative.NonLinears.Add(nonLinear);
                            }
                            break;
                        }
                        if (creative != null)
                        {
                            creative.AdId     = (string)vastCreative.Attribute("AdID");
                            creative.Id       = (string)vastCreative.Attribute("id");
                            creative.Sequence = ToNullableInt((string)vastCreative.Attribute("sequence"));
                            ad.Creatives.Add(creative);
                        }
                    }
                }
            }
        }
        internal static ICreative FindMatchingCreative(ICreative creative, Ad searchAd)
        {
            var type = creative.GetType();

            return(searchAd.Creatives.Where(c => c.GetType() == type).FirstOrDefault());
        }
        ActiveAdUnit CreateAdUnit(IEnumerable <ICreative> creativeSet, Ad ad, IAdSource adSource)
        {
            CreativeCompanions companions = null;
            var activeAdUnits             = new List <ActiveAdUnit>();

            foreach (var creative in creativeSet)
            {
                if (creative is CreativeLinear)
                {
                    var          linear             = (CreativeLinear)creative;
                    var          eligableMediaFiles = new Queue <MediaFile>(PrioritizeMedia(linear.MediaFiles));
                    ActiveAdUnit chosenAd           = null;
                    while (eligableMediaFiles.Any())
                    {
                        var media          = eligableMediaFiles.Dequeue();
                        var creativeSource = new LinearSource(linear, media);
                        var vpaid          = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                chosenAd = new ActiveAdUnit(creativeSource, vpaid, ad, adSource);
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }

                    if (chosenAd == null)
                    {
                        throw new LoadException(new Exception("Unable to find a player to play the linear ad."));
                    }
                    activeAdUnits.Add(chosenAd);
                }
                else if (creative is CreativeNonLinears)
                {
                    bool found      = false;
                    var  nonLinears = (CreativeNonLinears)creative;
                    foreach (var nonLinear in (nonLinears).NonLinears)
                    {
                        var creativeSource = new NonLinearSource(nonLinear, nonLinears);
                        var vpaid          = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                activeAdUnits.Add(new ActiveAdUnit(creativeSource, vpaid, ad, adSource));
                                found = true;
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }
                    if (!found)
                    {
                        throw new LoadException(new Exception("Unable to find a player to play any nonlinear ads."));
                    }
                }
                else if (creative is CreativeCompanions)
                {
                    companions = (CreativeCompanions)creative;
                }
                else /* not supported, ignore */ } {
        }
        return(activeAdUnits.FirstOrDefault());    // there should only be one linear or nonlinear ad in the sequenced group. Others are ignored.
    }
        private static void LoadCreatives(XElement adElement, Ad ad)
        {
            var vastCreatives = adElement.Element("Creatives");
            if (vastCreatives != null)
            {
                foreach (var vastCreative in vastCreatives.Elements("Creative"))
                {
                    var vastItem = vastCreative.Elements().FirstOrDefault();
                    if (vastItem != null)
                    {
                        ICreative creative = null;
                        switch (vastItem.Name.LocalName)
                        {
                            case "Linear":
                                var vastLinear = vastItem;
                                var linearCreative = new CreativeLinear();
                                creative = linearCreative;

                                foreach (var trackingEvent in GetTrackingEvents(vastLinear))
                                    linearCreative.TrackingEvents.Add(trackingEvent);

                                if (vastLinear.Elements("AdParameters").Any())
                                {
                                    var xmlEncoded = (bool?)vastLinear.Element("AdParameters").Attribute("xmlEncoded") ?? false;
                                    linearCreative.AdParameters = xmlEncoded ? XmlDecode((string)vastLinear.Element("AdParameters")) : (string)vastLinear.Element("AdParameters");
                                }

                                LoadVideoClicks(vastLinear, linearCreative);

                                if (vastLinear.Elements("CreativeExtensions").Any())
                                {
                                    foreach (var vastExtension in vastLinear.Element("CreativeExtensions").Elements("CreativeExtension"))
                                    {
                                        linearCreative.Extensions.Add(new Extension()); // TODO
                                    }
                                }

                                linearCreative.Duration = ToNullableTimeSpan((string)vastLinear.Element("Duration"));
                                linearCreative.SkipOffset = FlexibleOffset.Parse((string)vastLinear.Attribute("skipoffset"));

                                var vastIcons = vastLinear.Element("Icons");
                                if (vastIcons != null)
                                {
                                    foreach (var vastIcon in vastIcons.Elements("Icon"))
                                    {
                                        var icon = new Icon();

                                        if (vastIcon.Elements("IconClicks").Any())
                                        {
                                            var iconClicks = vastIcon.Element("IconClicks");

                                            icon.ClickThrough = GetUriValue(iconClicks.Element("IconClickThrough"));

                                            foreach (var clickTracking in iconClicks.Elements("IconClickTracking"))
                                            {
                                                icon.ClickTracking.Add((string)clickTracking);
                                            }
                                        }

                                        icon.ApiFramework = (string)vastIcon.Attribute("apiFramework") ?? string.Empty;
                                        icon.Duration = ToNullableTimeSpan((string)vastIcon.Attribute("duration")) ?? new TimeSpan?();
                                        icon.Height = (int?)vastIcon.Attribute("height");
                                        icon.Width = (int?)vastIcon.Attribute("width");
                                        icon.Offset = ToNullableTimeSpan((string)vastIcon.Attribute("offset")) ?? new TimeSpan?();
                                        icon.Program = (string)vastIcon.Attribute("program") ?? string.Empty;
                                        icon.XPosition = (string)vastIcon.Attribute("xPosition") ?? string.Empty;
                                        icon.YPosition = (string)vastIcon.Attribute("yPosition") ?? string.Empty;
                                        icon.Item = GetResources(vastIcon).FirstOrDefault();
                                        foreach (var viewTrackingUrl in vastIcon.Elements("IconViewTracking"))
                                        {
                                            icon.ViewTracking.Add((string)viewTrackingUrl);
                                        }
                                        linearCreative.Icons.Add(icon);
                                    }
                                }

                                if (vastLinear.Elements("MediaFiles").Any())
                                {
                                    foreach (var vastMedia in vastLinear.Element("MediaFiles").Elements("MediaFile"))
                                    {
                                        var mediaFile = new MediaFile();
                                        mediaFile.ApiFramework = (string)vastMedia.Attribute("apiFramework") ?? string.Empty;
                                        mediaFile.Bitrate = ToNullableLong((string)vastMedia.Attribute("bitrate"));
                                        mediaFile.Codec = (string)vastMedia.Attribute("codec") ?? string.Empty;
                                        mediaFile.Delivery = (MediaFileDelivery)Enum.Parse(typeof(MediaFileDelivery), (string)vastMedia.Attribute("delivery"), true);
                                        mediaFile.Height = (int?)vastMedia.Attribute("height") ?? 0;
                                        mediaFile.Width = (int?)vastMedia.Attribute("width") ?? 0;
                                        mediaFile.Id = (string)vastMedia.Attribute("id");
                                        mediaFile.MaintainAspectRatio = (bool?)vastMedia.Attribute("maintainAspectRatio") ?? false;
                                        mediaFile.MaxBitrate = (long?)vastMedia.Attribute("maxBitrate") ?? 0;
                                        mediaFile.MinBitrate = (long?)vastMedia.Attribute("minBitrate") ?? 0;
                                        mediaFile.Scalable = (bool?)vastMedia.Attribute("scalable") ?? false;
                                        mediaFile.Type = (string)vastMedia.Attribute("type");
                                        mediaFile.Value = GetUriValue(vastMedia);
                                        linearCreative.MediaFiles.Add(mediaFile);
                                    }
                                }
                                break;
                            case "CompanionAds":
                                var vastCompanions = vastItem;
                                creative = LoadCompanions(vastCompanions);
                                break;
                            case "NonLinearAds":
                                var vastNonLinears = vastItem;
                                var nonLinearCreative = new CreativeNonLinears();
                                creative = nonLinearCreative;

                                foreach (var trackingEvent in GetTrackingEvents(vastNonLinears))
                                    nonLinearCreative.TrackingEvents.Add(trackingEvent);

                                foreach (var vastNonLinear in vastNonLinears.Elements("NonLinear"))
                                {
                                    var nonLinear = new NonLinear();

                                    if (vastNonLinear.Elements("AdParameters").Any())
                                    {
                                        var xmlEncoded = (bool?)vastNonLinear.Element("AdParameters").Attribute("xmlEncoded") ?? false;
                                        nonLinear.AdParameters = xmlEncoded ? XmlDecode((string)vastNonLinear.Element("AdParameters")) : (string)vastNonLinear.Element("AdParameters");
                                    };
                                    nonLinear.ApiFramework = (string)vastNonLinear.Attribute("apiFramework");
                                    nonLinear.ClickThrough = GetUriValue(vastNonLinear.Element("NonLinearClickThrough"));
                                    foreach (var vastTracking in vastNonLinear.Elements("NonLinearClickTracking"))
                                    {
                                        nonLinear.ClickTracking.Add((string)vastTracking);
                                    }

                                    if (vastNonLinear.Elements("CreativeExtensions").Any())
                                    {
                                        foreach (var vastExtension in vastNonLinear.Element("CreativeExtensions").Elements("CreativeExtension"))
                                        {
                                            nonLinear.Extensions.Add(new Extension()); // TODO
                                        }
                                    }
                                    nonLinear.ExpandedHeight = (int?)vastNonLinear.Attribute("expandedHeight");
                                    nonLinear.ExpandedWidth = (int?)vastNonLinear.Attribute("expandedWidth");
                                    nonLinear.Height = (int?)vastNonLinear.Attribute("height") ?? 0;
                                    nonLinear.Width = (int?)vastNonLinear.Attribute("width") ?? 0;
                                    nonLinear.MaintainAspectRatio = (bool?)vastNonLinear.Attribute("maintainAspectRatio") ?? false;
                                    nonLinear.MinSuggestedDuration = ToNullableTimeSpan((string)vastNonLinear.Attribute("minSuggestedDuration")) ?? new TimeSpan?();
                                    nonLinear.Scalable = (bool?)vastNonLinear.Attribute("scalable") ?? false;
                                    nonLinear.Item = GetResources(vastNonLinear).FirstOrDefault();
                                    nonLinearCreative.NonLinears.Add(nonLinear);
                                }
                                break;
                        }
                        if (creative != null)
                        {
                            creative.AdId = (string)vastCreative.Attribute("AdID");
                            creative.Id = (string)vastCreative.Attribute("id");
                            creative.Sequence = ToNullableInt((string)vastCreative.Attribute("sequence"));
                            ad.Creatives.Add(creative);
                        }
                    }
                }
            }
        }
        internal static async Task<AdDocumentPayload> CreateFromVast1(XElement vastRoot, int? maxRedirectDepth, bool allowMultipleAds)
        {
            var result = new AdDocumentPayload();
            result.Version = (string)vastRoot.Attribute("version");

            foreach (var vastAdPod in vastRoot.Elements("Ad").GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
            {
                var adPod = new AdPod();
                foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                {
                    var ad = new Ad();
                    ad.Id = (string)vastAd.Attribute("id");

                    if (vastAd.Elements("InLine").Any())
                    {
                        throw new NotImplementedException();
                    }
                    else if (vastAd.Elements("Wrapper").Any())
                    {
                        Ad wrapper = new Ad();
                        var vastAdWrapper = vastAd.Element("Wrapper");

                        // parse the wrapper itself
                        wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                        var error = (string)vastAdWrapper.Element("Error");
                        if (error == null) wrapper.Errors.Add(error);

                        var linearCreative = new CreativeLinear();

                        foreach (var trackingEvent in GetTrackingEvents(vastAdWrapper))
                            linearCreative.TrackingEvents.Add(trackingEvent);

                        LoadVideoClicks(vastAdWrapper, linearCreative);

                        wrapper.Creatives.Add(linearCreative);

                        var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURL"));
                        if (vastAdUri != null)
                        {
                            // load the stream from the web
                            using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                            {
                                int? nextMaxRedirectDepth = maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth;
                                var vastDoc = await CreateFromVast(s, nextMaxRedirectDepth, allowMultipleAds);

                                var firstAdPodInChild = vastDoc.AdPods.FirstOrDefault();

                                if (firstAdPodInChild != null)
                                {
                                    // merge tracking info from this wrapper to every ad in the first adpod in the child
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                        MergeWrappedAd(wrapper, inlineAd);

                                    // add each ad from the first adpod in the child to the current adpod
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                        adPod.Ads.Add(inlineAd);
                                }
                            }
                        }
                    }
                }
                result.AdPods.Add(adPod);
            }
            return result;
        }
        static async Task<Ad> CreateLinearAd(FWAd source, FWAdReference reference)
        {
            var ad = new Ad();

            var allCallbacks = reference.EventCallbacks;
            foreach (var url in allCallbacks.Where(ec => ec.Type == FWCallbackType.Impression && ec.Name == FWEventCallback.DefaultImpression).SelectMany(ec => ec.GetUrls()))
            {
                ad.Impressions.Add(url);
            }

            int index = 0;
            IEnumerable<FWCreative> creatives = source.Creatives;
            if (reference.CreativeId != null)
            {
                creatives = creatives.Where(c => c.Id == reference.CreativeId);
            }
            foreach (var creative in creatives)
            {
                index++;
                var wrappedAds = new List<Ad>();
                var linear = new CreativeLinear();
                linear.AdParameters = string.Join("&", creative.Parameters.Select(p => Uri.EscapeDataString(p.Name) + "=" + Uri.EscapeDataString(p.Value)));
                linear.Duration = creative.Duration;
                linear.Sequence = index;

                IEnumerable<FWCreativeRendition> creativeRenditions = creative.CreativeRenditions;
                if (reference.CreativeRenditionId != null)
                {
                    creativeRenditions = creativeRenditions.Where(cr => cr.Id == reference.CreativeRenditionId).DefaultIfEmpty(creativeRenditions);
                    if (reference.ReplicaId != null)
                    {
                        creativeRenditions = creativeRenditions.Where(cr => cr.AdReplicaId == reference.ReplicaId).DefaultIfEmpty(creativeRenditions);
                    }
                }

                foreach (var rendition in creativeRenditions)
                {
                    if (!string.IsNullOrEmpty(rendition.WrapperType))
                    {
                        switch (rendition.WrapperType.ToLowerInvariant())
                        {
                            case "external/vast-2":
                                try
                                {
                                    var vastAdUri = new Uri(rendition.WrapperUrl);
                                    // load the stream from the web
                                    using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                                    {
                                        var wrappedVastDoc = await AdModelFactory.CreateFromVast(s, null, true);
                                        if (wrappedVastDoc != null)
                                        {
                                            // use the first ad
                                            var wrappedAd = wrappedVastDoc.AdPods.SelectMany(pod => pod.Ads).FirstOrDefault();
                                            if (wrappedAd != null)
                                            {
                                                wrappedAds.Add(wrappedAd);
                                            }
                                        }
                                    }
                                }
                                catch { /* swallow */ }
                                break;
                        }
                    }
                    else
                    {
                        // TODO: FreeWheel assets can contain Content instead of Url. This could be supported someday; for now it is ignored.
                        if (rendition.Asset != null)
                        {
                            var mediaFile = CreateMediaFile(creative, rendition, rendition.Asset);
                            if (mediaFile != null)
                            {
                                mediaFile.Ranking = (int)rendition.Preference + 1; // add one to indicate this is preferred over "OtherAssets"
                                linear.MediaFiles.Add(mediaFile);
                            }
                        }

                        foreach (var asset in rendition.OtherAssets)
                        {
                            var mediaFile = CreateMediaFile(creative, rendition, asset);
                            if (mediaFile != null)
                            {
                                mediaFile.Ranking = (int)rendition.Preference;
                                linear.MediaFiles.Add(mediaFile);
                            }
                        }
                    }
                }

                // generate callback urls from one base url
                foreach (var eventCallback in allCallbacks.Where(ec => ec.Type == FWCallbackType.Impression))
                {
                    foreach (var url in eventCallback.GetUrls())
                    {
                        switch (eventCallback.Name.ToLower())
                        {
                            case "start":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Start, Value = url });
                                break;
                            case "firstquartile":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.FirstQuartile, Value = url });
                                break;
                            case "midpoint":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Midpoint, Value = url });
                                break;
                            case "thirdquartile":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.ThirdQuartile, Value = url });
                                break;
                            case "complete":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Complete, Value = url });
                                break;
                        }
                    }
                }

                // generate callback urls from one base url
                foreach (var eventCallback in allCallbacks.Where(ec => ec.Type == FWCallbackType.Standard))
                {
                    foreach (var url in eventCallback.GetUrls())
                    {
                        switch (eventCallback.Name.Replace("-", "").ToLower())
                        {
                            case "_creativeview":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.CreativeView, Value = url });
                                break;
                            case "_mute":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Mute, Value = url });
                                break;
                            case "_unmute":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Unmute, Value = url });
                                break;
                            case "_pause":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Pause, Value = url });
                                break;
                            case "_rewind":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Rewind, Value = url });
                                break;
                            case "_resume":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Resume, Value = url });
                                break;
                            case "_fullscreen":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Fullscreen, Value = url });
                                break;
                            case "_exitfullscreen":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.ExitFullscreen, Value = url });
                                break;
                            case "_expand":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Expand, Value = url });
                                break;
                            case "_collapse":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Collapse, Value = url });
                                break;
                            case "_acceptinvitation":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.AcceptInvitation, Value = url });
                                break;
                            case "_close":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Close, Value = url });
                                break;
                            case "_skip":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Skip, Value = url });
                                break;
                            case "_progress":
                                linear.TrackingEvents.Add(new TrackingEvent() { Type = TrackingType.Progress, Value = url });
                                break;
                        }
                    }
                }

                foreach (var url in allCallbacks.Where(ec => ec.Type == FWCallbackType.Click && !ec.ShowBrowser).SelectMany(ec => ec.GetUrls()))
                {
                    linear.ClickTracking.Add(url);
                }

                var clickUrl = allCallbacks.Where(ec => ec.Type == FWCallbackType.Click && ec.ShowBrowser).SelectMany(ec => ec.GetUrls()).FirstOrDefault();
                if (clickUrl != null)
                {
                    linear.ClickThrough = new Uri(clickUrl);
                }

                // generate callback urls from one base url ONLY when the callback does not already exist
                foreach (var eventCallback in allCallbacks.Where(ec => ec.Type == FWCallbackType.Generic))
                {
                    foreach (var url in eventCallback.GetUrls())
                    {
                        var baseUrl = url + string.Format("&metr={0}", FreeWheelFactory.GetSupportedMetrics());

                        // quartile events
                        var quartileUrl = baseUrl + "&ct=[LASTQUARTILE]&et=i"; // [LASTQUARTILE] will get replaced by the VPAID controller
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.FirstQuartile, Value = quartileUrl + "&cn=firstQuartile" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Midpoint, Value = quartileUrl + "&cn=midPoint" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.ThirdQuartile, Value = quartileUrl + "&cn=thirdQuartile" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Complete, Value = quartileUrl + "&cn=complete" });

                        // advanced metrics
                        var advancedUrl = baseUrl + "&et=s";
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Mute, Value = advancedUrl + "&cn=_mute" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Unmute, Value = advancedUrl + "&cn=_un-mute" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Collapse, Value = advancedUrl + "&cn=_collapse" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Expand, Value = advancedUrl + "&cn=_expand" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Pause, Value = advancedUrl + "&cn=_pause" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Resume, Value = advancedUrl + "&cn=_resume" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Rewind, Value = advancedUrl + "&cn=_rewind" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.AcceptInvitation, Value = advancedUrl + "&cn=_accept-invitation" });
                        AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Close, Value = advancedUrl + "&cn=_close" });
                        //AddSecondaryCallback(linear.TrackingEvents, new TrackingEvent() { Type = TrackingType.Minimize, Value = advancedUrl + "&cn=_minimize" });
                    }
                }

                ad.Creatives.Add(linear);

                foreach (var wrappedAd in wrappedAds)
                {
                    AdModelFactory.MergeWrappedAdBeacons(wrappedAd, ad);
                    var wrappedCreative = AdModelFactory.FindMatchingCreative(linear, wrappedAd);
                    AdModelFactory.MergeWrappedCreative(wrappedCreative, linear);
                }
            }

            return ad;
        }
        ActiveAdUnit CreateAdUnit(IEnumerable<ICreative> creativeSet, Ad ad, IAdSource adSource)
        {
            CreativeCompanions companions = null;
            var activeAdUnits = new List<ActiveAdUnit>();
            foreach (var creative in creativeSet)
            {
                if (creative is CreativeLinear)
                {
                    var linear = (CreativeLinear)creative;
                    var eligableMediaFiles = new Queue<MediaFile>(PrioritizeMedia(linear.MediaFiles));
                    ActiveAdUnit chosenAd = null;
                    while (eligableMediaFiles.Any())
                    {
                        var media = eligableMediaFiles.Dequeue();
                        var creativeSource = new LinearSource(linear, media);
                        var vpaid = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                chosenAd = new ActiveAdUnit(creativeSource, vpaid, ad, adSource);
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }

                    if (chosenAd == null) throw new LoadException(new Exception("Unable to find a player to play the linear ad."));
                    activeAdUnits.Add(chosenAd);
                }
                else if (creative is CreativeNonLinears)
                {
                    bool found = false;
                    var nonLinears = (CreativeNonLinears)creative;
                    foreach (var nonLinear in (nonLinears).NonLinears)
                    {
                        var creativeSource = new NonLinearSource(nonLinear, nonLinears);
                        var vpaid = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                activeAdUnits.Add(new ActiveAdUnit(creativeSource, vpaid, ad, adSource));
                                found = true;
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }
                    if (!found)
                    {
                        throw new LoadException(new Exception("Unable to find a player to play any nonlinear ads."));
                    }
                }
                else if (creative is CreativeCompanions)
                {
                    companions = (CreativeCompanions)creative;
                }
                else { /* not supported, ignore */ }
            }
            return activeAdUnits.FirstOrDefault(); // there should only be one linear or nonlinear ad in the sequenced group. Others are ignored.
        }
        async Task StartAdUnitAsync(ActiveAdUnit adUnit, Ad ad, CancellationToken cancellationToken)
        {
            // start the ad
            await VpaidController.StartAdAsync(adUnit, cancellationToken);

            // fire the impression beacon
            foreach (var url in ad.Impressions)
            {
                VpaidController.TrackUrl(url, adUnit.CreativeSource);
            }
        }
        async Task<ActiveAdUnit> LoadAdUnitAsync(ICollection<ICreative> creativeSet, Ad ad, IAdSource adSource, CancellationToken cancellationToken)
        {
            bool preloaded = false;
            ActiveAdUnit result = null;

            // check to see if there are any pre-loading ads.
            if (loadOperation != null)
            {
                try
                {
                    if (loadOperation.AdSource == adSource)
                    {
                        cancellationToken.Register(() =>
                        {
                            if (loadOperation != null)
                            {
                                // no need to wait
                                var dummyTask = loadOperation.CancelAsync();
                            }
                        });
                        result = await loadOperation.Task;
                        preloaded = true;
                    }
                    else
                    {
                        await loadOperation.CancelAsync();
                    }
                }
                catch { /* ignore, we'll try again regardless of reason, it was just a preload optimization anyway */ }
                finally
                {
                    loadOperation = null;
                }
                cancellationToken.ThrowIfCancellationRequested();
            }
            if (result == null)
            {
                result = CreateAdUnit(creativeSet, ad, adSource);
            }
            if (result == null) throw new LoadException(new Exception("No ad unit could be created"));

            if (!preloaded)
            {
                // Start initializing the ad. It is OK if there are other ads playing still.
                loadOperation = LoadAdUnit(result, cancellationToken);
                try
                {
                    await loadOperation.Task;
                }
                catch (OperationCanceledException) { throw; }
                catch (Exception ex) { throw new LoadException(ex); }
                finally
                {
                    loadOperation = null;
                }
                cancellationToken.ThrowIfCancellationRequested();
            }

            return result;
        }
Example #14
0
        internal static async Task <AdDocumentPayload> CreateFromVast1(XElement vastRoot, int?maxRedirectDepth, bool allowMultipleAds)
        {
            var result = new AdDocumentPayload();

            result.Version = (string)vastRoot.Attribute("version");

            foreach (var vastAdPod in vastRoot.Elements("Ad").GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
            {
                var adPod = new AdPod();
                foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                {
                    var ad = new Ad();
                    ad.Id = (string)vastAd.Attribute("id");

                    if (vastAd.Elements("InLine").Any())
                    {
                        throw new NotImplementedException();
                    }
                    else if (vastAd.Elements("Wrapper").Any())
                    {
                        Ad  wrapper       = new Ad();
                        var vastAdWrapper = vastAd.Element("Wrapper");

                        // parse the wrapper itself
                        wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                        var error = (string)vastAdWrapper.Element("Error");
                        if (error == null)
                        {
                            wrapper.Errors.Add(error);
                        }

                        var linearCreative = new CreativeLinear();

                        foreach (var trackingEvent in GetTrackingEvents(vastAdWrapper))
                        {
                            linearCreative.TrackingEvents.Add(trackingEvent);
                        }

                        LoadVideoClicks(vastAdWrapper, linearCreative);

                        wrapper.Creatives.Add(linearCreative);

                        var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURL"));
                        if (vastAdUri != null)
                        {
                            // load the stream from the web
                            using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                            {
                                int?nextMaxRedirectDepth = maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth;
                                var vastDoc = await CreateFromVast(s, nextMaxRedirectDepth, allowMultipleAds);

                                var firstAdPodInChild = vastDoc.AdPods.FirstOrDefault();

                                if (firstAdPodInChild != null)
                                {
                                    // merge tracking info from this wrapper to every ad in the first adpod in the child
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                    {
                                        MergeWrappedAd(wrapper, inlineAd);
                                    }

                                    // add each ad from the first adpod in the child to the current adpod
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                    {
                                        adPod.Ads.Add(inlineAd);
                                    }
                                }
                            }
                        }
                    }
                }
                result.AdPods.Add(adPod);
            }
            return(result);
        }
        private static ICreative FindMatchingCreative(Ad ad, ICreative creative, Ad wrapperAd)
        {
            Type type = creative.GetType();

            var appropriateCreatives = ad.Creatives.Where(c => c.GetType() == type).ToList();
            var appropriateWrapperCreatives = wrapperAd.Creatives.Where(c => c.GetType() == type).ToList();

            int index = appropriateCreatives.IndexOf(creative);

            if (appropriateWrapperCreatives.Count > index)
                return appropriateWrapperCreatives.ElementAt(index);
            else
                return null;
        }
 internal static ICreative FindMatchingCreative(ICreative creative, Ad searchAd)
 {
     var type = creative.GetType();
     return searchAd.Creatives.Where(c => c.GetType() == type).FirstOrDefault();
 }
Example #17
0
        internal static async Task <AdDocumentPayload> CreateFromVast(Stream stream, int?maxRedirectDepth, bool allowMultipleAds)
#endif
        {
            XDocument xDoc     = XDocument.Load(stream);
            XElement  vastRoot = xDoc.Element("VAST");

            if (vastRoot == null)
            {
                vastRoot = xDoc.Element("VideoAdServingTemplate");
                if (vastRoot == null)
                {
                    throw new NotImplementedException();
                }
                return(await CreateFromVast1(vastRoot, maxRedirectDepth, allowMultipleAds));
            }
            else
            {
                var result = new AdDocumentPayload();
                result.Version = (string)vastRoot.Attribute("version");
                result.Error   = (string)vastRoot.Element("Error");

                var eligableAds = vastRoot.Elements("Ad");
                if (!allowMultipleAds)
                {
                    eligableAds = eligableAds.Where(va => string.IsNullOrEmpty((string)va.Attribute("sequence")));
                }
                foreach (var vastAdPod in eligableAds.GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
                {
                    var adPod = new AdPod();
                    foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                    {
                        var ad = new Ad();
                        ad.Id = (string)vastAd.Attribute("id");

                        if (vastAd.Elements("InLine").Any())
                        {
                            var vastAdInline = vastAd.Element("InLine");

                            ad.AdSystem = GetAdSystem(vastAdInline.Element("AdSystem"));

                            ad.Advertiser  = (string)vastAdInline.Element("Advertiser");
                            ad.Description = (string)vastAdInline.Element("Description");
                            var error = (string)vastAdInline.Element("Error");
                            if (error != null)
                            {
                                ad.Errors.Add(error);
                            }
                            ad.Title  = (string)vastAdInline.Element("AdTitle");
                            ad.Survey = GetUriValue(vastAdInline.Element("Survey"));

                            ad.Pricing = new Pricing();
                            var pricing = vastAdInline.Element("Pricing");
                            if (pricing != null)
                            {
                                ad.Pricing.Currency = (string)pricing.Attribute("currency");
                                ad.Pricing.Model    = (PricingModel)Enum.Parse(typeof(PricingModel), (string)pricing.Attribute("model"), true);
                                ad.Pricing.Value    = Convert.ToDouble((string)pricing);
                            }

                            foreach (var vastImpression in vastAdInline.Elements("Impression"))
                            {
                                ad.Impressions.Add((string)vastImpression);
                            }

                            if (vastAdInline.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdInline.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            LoadCreatives(vastAdInline, ad);

                            adPod.Ads.Add(ad);
                        }
                        else if (vastAd.Elements("Wrapper").Any())
                        {
                            Ad  wrapper       = new Ad();
                            var vastAdWrapper = vastAd.Element("Wrapper");

                            // parse the wrapper itself
                            wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                            var error = (string)vastAdWrapper.Element("Error");
                            if (error != null)
                            {
                                wrapper.Errors.Add(error);
                            }

                            foreach (var vastImpression in vastAdWrapper.Elements("Impression"))
                            {
                                wrapper.Impressions.Add((string)vastImpression);
                            }

                            LoadCreatives(vastAdWrapper, wrapper);

                            if (vastAdWrapper.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdWrapper.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            AdDocumentPayload wrappedVastDoc = null;
                            var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURI"));
                            if (vastAdUri != null && (!maxRedirectDepth.HasValue || maxRedirectDepth.Value > 0))
                            {
                                try
                                {
                                    // load the stream from the web
                                    using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                                    {
                                        var newAllowMultipleAds      = vastAdWrapper.GetBoolAttribute("allowMultipleAds", allowMultipleAds);
                                        var followAdditionalWrappers = vastAdWrapper.GetBoolAttribute("followAdditionalWrappers", true);
                                        int?nextMaxRedirectDepth     = followAdditionalWrappers ? (maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth) : 0;
                                        wrappedVastDoc = await CreateFromVast(s, nextMaxRedirectDepth, newAllowMultipleAds);
                                    }
                                }
                                catch { /* swallow */ }
                            }

                            AdPod wrappedAdPod = null;
                            if (wrappedVastDoc != null)
                            {
                                wrappedAdPod = wrappedVastDoc.AdPods.FirstOrDefault();
                            }

                            if (wrappedAdPod == null || !wrappedAdPod.Ads.Any())
                            {
                                // no ads were returned
                                var fallbackOnNoAd = vastAdWrapper.GetBoolAttribute("fallbackOnNoAd", true);
                                if (fallbackOnNoAd)
                                {
                                    wrappedAdPod = FallbackAdPod;
                                }
                            }

                            if (wrappedAdPod != null)
                            {
                                // merge tracking info from this wrapper to every ad in the first adpod in the child
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                {
                                    MergeWrappedAd(wrapper, inlineAd);
                                }

                                // add each ad from the first adpod in the child to the current adpod
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                {
                                    adPod.Ads.Add(inlineAd);
                                }
                            }
                        }
                    }
                    result.AdPods.Add(adPod);
                }
                return(result);
            }
        }
 private static AdDocumentPayload CreateDocument(IClipAdPayload mediaSource)
 {
     var adDocument = new AdDocumentPayload();
     var adPod = new AdPod();
     var ad = new Ad();
     var linearCreative = new CreativeLinear();
     linearCreative.ClickThrough = mediaSource.ClickThrough;
     linearCreative.MediaFiles.Add(new MediaFile() { Type = mediaSource.MimeType, Value = mediaSource.MediaSource });
     ad.Creatives.Add(linearCreative);
     adPod.Ads.Add(ad);
     adDocument.AdPods.Add(adPod);
     return adDocument;
 }
        async Task <ActiveAdUnit> LoadAdUnitAsync(ICollection <ICreative> creativeSet, Ad ad, IAdSource adSource, CancellationToken cancellationToken)
        {
            bool         preloaded = false;
            ActiveAdUnit result    = null;

            // check to see if there are any pre-loading ads.
            if (loadOperation != null)
            {
                try
                {
                    if (loadOperation.AdSource == adSource)
                    {
                        cancellationToken.Register(() =>
                        {
                            if (loadOperation != null)
                            {
                                // no need to wait
                                var dummyTask = loadOperation.CancelAsync();
                            }
                        });
                        result    = await loadOperation.Task;
                        preloaded = true;
                    }
                    else
                    {
                        await loadOperation.CancelAsync();
                    }
                }
                catch { /* ignore, we'll try again regardless of reason, it was just a preload optimization anyway */ }
                finally
                {
                    loadOperation = null;
                }
                cancellationToken.ThrowIfCancellationRequested();
            }
            if (result == null)
            {
                result = CreateAdUnit(creativeSet, ad, adSource);
            }
            if (result == null)
            {
                throw new LoadException(new Exception("No ad unit could be created"));
            }

            if (!preloaded)
            {
                // Start initializing the ad. It is OK if there are other ads playing still.
                loadOperation = LoadAdUnit(result, cancellationToken);
                try
                {
                    await loadOperation.Task;
                }
                catch (OperationCanceledException) { throw; }
                catch (Exception ex) { throw new LoadException(ex); }
                finally
                {
                    loadOperation = null;
                }
                cancellationToken.ThrowIfCancellationRequested();
            }

            return(result);
        }