// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.core.http.policy;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.function.Supplier;

/**
 * This class handles adding SPI plug-able policies to a pipeline automatically.
 */
public final class HttpPolicyProviders {

    private static final Map<Class<? extends HttpPolicyProvider>, ServiceLoader<? extends HttpPolicyProvider>>
        SERVICE_LOADERS = new HashMap<>();

    private HttpPolicyProviders() {
        // no-op
    }

    /**
     * Adds SPI policies that implement {@link BeforeRetryPolicyProvider}.
     *
     * @param policies Policy list to append the policies.
     */
    public static void addBeforeRetryPolicies(List<HttpPipelinePolicy> policies) {
        addRetryPolicies(policies, () -> getPolicyProviders(false, BeforeRetryPolicyProvider.class));
    }

    /**
     * Adds SPI policies that implement {@link AfterRetryPolicyProvider}.
     *
     * @param policies Policy list to append the policies.
     */
    public static void addAfterRetryPolicies(List<HttpPipelinePolicy> policies) {
        addRetryPolicies(policies, () -> getPolicyProviders(false, AfterRetryPolicyProvider.class));
    }

    private static void addRetryPolicies(List<HttpPipelinePolicy> policies,
        Supplier<Iterator<? extends HttpPolicyProvider>> policySupplier) {
        Iterator<? extends HttpPolicyProvider> it = policySupplier.get();
        while (it.hasNext()) {
            HttpPolicyProvider policyProvider = it.next();
            HttpPipelinePolicy policy = policyProvider.create();
            if (policy == null) {
                throw new NullPointerException("HttpPipelinePolicy created with " + policyProvider.getClass()
                    + " resulted in a null policy");
            }
            policies.add(policy);
        }
    }

    private static Iterator<? extends HttpPolicyProvider> getPolicyProviders(boolean reload,
        Class<? extends HttpPolicyProvider> cls) {
        ServiceLoader<? extends HttpPolicyProvider> serviceLoader = SERVICE_LOADERS
            .computeIfAbsent(cls, ServiceLoader::load);

        if (reload) {
            serviceLoader.reload();
        }

        return serviceLoader.iterator();
    }
}

@@ -89,16 +89,17 @@
 icense.%0A
+%0A
 package 
@@ -131,61 +131,8 @@
 y;%0A%0A
-import java.util.HashMap;%0Aimport java.util.Iterator;%0A
 impo
@@ -154,30 +154,8 @@
 st;%0A
-import java.util.Map;%0A
 impo
@@ -185,44 +185,8 @@
 der;
-%0Aimport java.util.function.Supplier;
 %0A%0A/*
@@ -313,17 +313,16 @@
 iders %7B%0A
-%0A
     priv
@@ -342,77 +342,322 @@
 nal 
-Map%3CClass%3C? extends HttpPolicyProvider%3E, ServiceLoader%3C? extends Http
+String INVALID_POLICY = %22HttpPipelinePolicy created with %25s resulted in a null policy.%22;%0A%0A    private static final Iterable%3CBeforeRetryPolicyProvider%3E BEFORE_PROVIDER;%0A    private static final Iterable%3CAfterRetryPolicyProvider%3E AFTER_PROVIDER;%0A%0A    static %7B%0A        BEFORE_PROVIDER = ServiceLoader.load(BeforeRetry
 Poli
@@ -666,18 +666,24 @@
 Provider
-%3E%3E
+.class);
 %0A       
@@ -687,42 +687,82 @@
     
-SERVICE_LOA
+AFTER_PROVI
 DER
-S
  = 
-new HashMap%3C%3E();
+ServiceLoader.load(AfterRetryPolicyProvider.class);%0A    %7D
 %0A%0A  
@@ -1066,35 +1066,29 @@
 %0A        add
-Retry
 Polic
-i
 es(policies,
@@ -1092,72 +1092,23 @@
 es, 
-() -%3E getPolicyProviders(false, BeforeRetryPolicyProvider.class)
+BEFORE_PROVIDER
 );%0A 
@@ -1362,27 +1362,21 @@
      add
-Retry
 Polic
-i
 es(polic
@@ -1384,71 +1384,22 @@
 es, 
-() -%3E getPolicyProviders(false, AfterRetryPolicyProvider.class)
+AFTER_PROVIDER
 );%0A 
@@ -1431,27 +1431,21 @@
 void add
-Retry
 Polic
-i
 es(List%3C
@@ -1486,25 +1486,16 @@
     
-Supplier%3C
 Itera
-tor
+ble
 %3C? e
@@ -1524,24 +1524,18 @@
 der%3E
-%3E
  p
-olicySuppli
+rovid
 er
+s
 ) %7B%0A
@@ -1546,163 +1546,55 @@
     
-Iterator%3C? extends HttpPolicyProvider%3E it = policySupplier.get();%0A        while (it.hasNext()) %7B%0A            HttpPolicyProvider policyProvider = it.next();
+for (HttpPolicyProvider provider : providers) %7B
 %0A   
@@ -1631,22 +1631,16 @@
 licy = p
-olicyP
 rovider.
@@ -1734,121 +1734,58 @@
 ion(
-%22HttpPipelinePolicy created with %22 + policyProvider.getClass()%0A                    + %22 resulted in a null policy%22
+String.format(INVALID_POLICY, provider.getClass())
 );%0A 
@@ -1793,24 +1793,25 @@
           %7D%0A
+%0A
             
@@ -1851,409 +1851,7 @@
    %7D
-%0A%0A    private static Iterator%3C? extends HttpPolicyProvider%3E getPolicyProviders(boolean reload,%0A        Class%3C? extends HttpPolicyProvider%3E cls) %7B%0A        ServiceLoader%3C? extends HttpPolicyProvider%3E serviceLoader = SERVICE_LOADERS%0A            .computeIfAbsent(cls, ServiceLoader::load);%0A%0A        if (reload) %7B%0A            serviceLoader.reload();%0A        %7D%0A%0A        return serviceLoader.iterator();%0A    %7D
 %0A%7D%0A

Maintain Before and After Retry Polices Separately in HttpPolicyProviders (#8185)