/// <summary>
        /// Creates an instance of CustomSecurityTokenServiceConfiguration.
        /// </summary>
        public CustomSecurityTokenServiceConfiguration(string idp) : base(idp)
        {
            CustomAdsTextTraceSource ts = new CustomAdsTextTraceSource("IdpAds.CustomSecurityTokenServiceConfiguration.CustomSecurityTokenServiceConfiguration",
               "AdsTraceSource", SourceLevels.Information);

            SecurityTokenService = typeof(CustomSecurityTokenService);

            // GFIPM S2S 8.8.2.6.e Modify the "NotOnOrAfter"
            this.DefaultTokenLifetime = TimeSpan.FromSeconds(Constants.AdsAssertionValidityPeriod);

            ts.TraceInformation("DefaultTokenLifetime: " + this.DefaultTokenLifetime.TotalSeconds.ToString());
        }
        // Handle Condition/Delegates
        protected override IClaimsIdentity CreateClaims(Saml2SecurityToken samlToken)
        {
            CustomAdsTextTraceSource ts = new CustomAdsTextTraceSource("IdpAds.OnBehalfOfSaml2SecurityTokenHandler.CreateClaims",
                "AdsTraceSource", SourceLevels.Information);

   
            // process subject in base 
            IClaimsIdentity subject = base.CreateClaims(samlToken);
            
            Saml2Assertion assertion = samlToken.Assertion;

            // process Condition/Delegation            
            ts.TraceInformation("assertion.Conditions Type: " + assertion.Conditions.GetType().Name);

            if (assertion.Conditions is Saml2ConditionsDelegateWrapper)
            {
                Saml2ConditionsDelegateWrapper delegateData = assertion.Conditions as Saml2ConditionsDelegateWrapper;

                 //iterate over the Condition delegate elements
                 //Check if there are delegates within an incoming assertion                                
                IClaimsIdentity currentSubject = subject;
               
                if (delegateData != null && delegateData.Delegates != null)
                {
                    // Add the delegates in the DelegationRestrictionType
                    ts.TraceInformation("Number of Delegates: " + delegateData.Delegates.Delegate.Length);
                    for (int i = 0; i < delegateData.Delegates.Delegate.Length; i++)
                    {
                        DelegateType del = delegateData.Delegates.Delegate[i];
                                                                        
                        if (del != null)
                        {
                            string nameId = del.Item.ToString();

                            var claims = new List<Claim>();
                            claims.Add(new Claim(ClaimTypes.Name, nameId));
                            claims.Add(new Claim(ClaimTypes.AuthenticationInstant, XmlConvert.ToString(del.DelegationInstant, DateTimeFormats.Generated)));

                            // now add to current subject
                            currentSubject.Actor = new ClaimsIdentity(claims);

                            currentSubject = currentSubject.Actor;
                        }
                    }
                }
            }

            return subject;
        }
        //string TokenToString(SecurityToken token)
        //{
        //    StringBuilder stringBuilder = new StringBuilder();
        //    XmlWriter xr = XmlWriter.Create(new StringWriter(stringBuilder), new XmlWriterSettings { OmitXmlDeclaration = true });
        //    this.WriteToken(xr, token);
        //    xr.Flush();

        //    return stringBuilder.ToString();
        //}

        //void SerializeTokenToStream(Saml2SecurityToken saml2Token, StreamWriter file, string section)
        //{
        //    // serialize the token
        //    file.WriteLine(section);

        //    if (saml2Token.Assertion.Issuer != null)
        //    {
        //        file.WriteLine("\tIssuer: " + saml2Token.Assertion.Issuer.Value);
        //    }
        //    else
        //    {
        //        file.WriteLine("Saml 2 Assertion Issuer: NULL");
        //    }

        //    if (saml2Token.Assertion.Issuer != null)
        //    {
        //        file.WriteLine("\tSigningCredentials: " + saml2Token.Assertion.SigningCredentials.ToString());
        //    }
        //    else
        //    {
        //        file.WriteLine("Saml 2 Assertion SigningCredentials: NULL");
        //    }

        //    if (saml2Token.Assertion.Subject != null)
        //    {
        //        if (saml2Token.Assertion.Subject.NameId != null)
        //        {
        //            file.WriteLine("\tSubject NameId: " + saml2Token.Assertion.Subject.NameId.Value);
        //        }
        //        else
        //        {
        //            file.WriteLine("\tSubject NameId: NULL");
        //        }
        //    }
        //    else
        //    {
        //        file.WriteLine("Saml 2 Assertion Subject: NULL");
        //    }

        //    file.WriteLine("Saml Token:\n");

        //    file.WriteLine(TokenToString(saml2Token));

        //}
        #endregion

        public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
        {
            CustomAdsTextTraceSource ts = new CustomAdsTextTraceSource("IdpAds.OnBehalfOfSaml2SecurityTokenHandler.ValidateToken",
                "AdsTraceSource", SourceLevels.Information);

            Saml2SecurityToken saml2Token = token as Saml2SecurityToken;
            if (saml2Token != null && saml2Token.Assertion != null)
            {
                // GFIPM S2S 8.8.2.5.a.v
                Saml2AuthenticationStatement authnStatement = null;

                foreach (Saml2Statement st in saml2Token.Assertion.Statements)
                {
                    authnStatement = st as Saml2AuthenticationStatement;
                    if (authnStatement != null)
                    {
                        break;
                    }
                }

                if (authnStatement != null)
                {
                    ts.TraceInformation("Assertion Age: " + DateTime.UtcNow.Subtract(authnStatement.AuthenticationInstant).TotalSeconds.ToString() + " seconds");
                    ts.TraceInformation("Max Assertion Age:" + TimeSpan.FromSeconds(Constants.AdsMaxAssertionAge).TotalHours.ToString() + " hours");

                    if (DateTime.UtcNow.Subtract(authnStatement.AuthenticationInstant) > TimeSpan.FromSeconds(Constants.AdsMaxAssertionAge))
                    {
                        throw new SecurityTokenException("The authentication instant of the SAML2 assertion cannot be older than " 
                            + TimeSpan.FromSeconds(Constants.AdsMaxAssertionAge).TotalHours.ToString() + " hours");
                    }
                }
                else
                {
                    ts.TraceInformation("authnStatement: NULL");
                }


                // TODO: Refactor

                X509Certificate2 signingCert = Common.CertificateUtil.GetCertificate(StoreName.My, 
                    StoreLocation.LocalMachine, WebConfigurationManager.AppSettings["SigningCertificateName"]);

                // TODO: What if the KeyIdentifierClause changes?
                // How to prevent recompiling?

                SecurityKeyIdentifier secKeyIdentifier = saml2Token.Assertion.SigningCredentials.SigningKeyIdentifier;

                SecurityKeyIdentifierClause keyIdentifierClause = secKeyIdentifier.Find<X509RawDataKeyIdentifierClause>();

                if (keyIdentifierClause is X509RawDataKeyIdentifierClause)
                {
                    //X509SubjectKeyIdentifierClause x509IdentClause = keyIdentifierClause as X509SubjectKeyIdentifierClause;
                    X509RawDataKeyIdentifierClause x509IdentClause = keyIdentifierClause as X509RawDataKeyIdentifierClause;
            
                    // GFIPM S2S 8.8.2.5.a.iv
                    if (!x509IdentClause.Matches(signingCert))
                    {
                        ts.TraceInformation("The OnBehalfOf SAML 2 assertion was not signed by this IDP");

                        throw new SecurityTokenException("The OnBehalfOf SAML 2 assertion was not signed by this IDP");
                    }
                }

                // Seems this is not needed! See note below
                #region Deprecate?
                if (saml2Token.Assertion.Subject != null && saml2Token.Assertion.Subject.SubjectConfirmations != null)
                {
                    // first serialize before
                    //SerializeTokenToStream(saml2Token, file, "Saml 2 Assertion Before Removing: ");

                    ts.TraceInformation("Saml 2 Assertion: SubjectConfirmations");

                    // GFIPM S2S 8.8.2.6.b: Remove <SubjectConfirmationData> if present
                    // TODO: Need to test Holder-Of-Key confirmation method to make sure this works.
                    // TODO: This is actually not needed. When the new assertion is created, the 
                    // <SubjectConfirmationData> element is not created which is the same effect as deleting it.
                    for (int i = 0; i < saml2Token.Assertion.Subject.SubjectConfirmations.Count; i++)
                    {                        
                        saml2Token.Assertion.Subject.SubjectConfirmations[i].SubjectConfirmationData = null;
                    }
                }
                #endregion
            }
                        
            return base.ValidateToken(token);
        }