/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2015 Richard "Shred" Körber
 *   http://acme4j.shredzone.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.shredzone.acme4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.shredzone.acme4j.challenge.Challenge;

/**
 * Represents an authorization request at the ACME server.
 *
 * @author Richard "Shred" Körber
 */
public class Authorization {

    private String domain;
    private String status;
    private Date expires;
    private List<Challenge> challenges;
    private List<List<Challenge>> combinations;

    /**
     * Gets the domain name to be authorized.
     */
    public String getDomain() {
        return domain;
    }

    /**
     * Sets the domain name to authorize.
     */
    public void setDomain(String domain) {
        this.domain = domain;
    }

    /**
     * Gets the authorization status.
     */
    public String getStatus() {
        return status;
    }

    /**
     * Sets the authorization status.
     */
    public void setStatus(String status) {
        this.status = status;
    }

    /**
     * Gets the expiry date of the authorization, if set by the server.
     */
    public Date getExpires() {
        return expires;
    }

    /**
     * Sets the expiry date of the authorization.
     */
    public void setExpires(Date expires) {
        this.expires = expires;
    }

    /**
     * Gets a list of all challenges available by the server.
     */
    public List<Challenge> getChallenges() {
        return challenges;
    }

    /**
     * Sets a list of all challenges available by the server.
     */
    public void setChallenges(List<Challenge> challenges) {
        this.challenges = challenges;
    }

    /**
     * Gets all combinations of challenges supported by the server.
     */
    public List<List<Challenge>> getCombinations() {
        return combinations;
    }

    /**
     * Sets all combinations of challenges supported by the server.
     */
    public void setCombinations(List<List<Challenge>> combinations) {
        this.combinations = combinations;
    }

    /**
     * Finds a single {@link Challenge} of the given type. Responding to this
     * {@link Challenge} is sufficient for authorization. This is a convenience call to
     * {@link #findCombination(String...)}.
     *
     * @param type
     *            Challenge name (e.g. "http-01")
     * @return {@link Challenge} matching that name, or {@code null} if there is no such
     *         challenge, or the challenge alone is not sufficient for authorization.
     * @throws ClassCastException
     *             if the type does not match the expected Challenge class type
     */
    @SuppressWarnings("unchecked")
    public <T extends Challenge> T findChallenge(String type) {
        Collection<Challenge> result = findCombination(type);
        return (result != null ? (T) result.iterator().next() : null);
    }

    /**
     * Finds a combination of {@link Challenge} types that the client supports. The client
     * has to respond to <em>all</em> of the {@link Challenge}s returned.
     *
     * @param types
     *            Challenge name or names (e.g. "http-01"), in no particular order.
     * @return Matching {@link Challenge} combination, or {@code null} if the ACME server
     *         does not support this challenge combination. The challenges are returned
     *         in no particular order.
     */
    public Collection<Challenge> findCombination(String... types) {
        Collection<String> reference = Arrays.asList(types);

        for (List<Challenge> combination : combinations) {
            Collection<String> combinationTypes = new ArrayList<>();
            for (Challenge c : combination) {
                combinationTypes.add(c.getType());
            }

            if (reference.size() == combinationTypes.size()
                    && reference.containsAll(combinationTypes)) {
                return combination;
            }
        }

        return null;
    }

}

@@ -3481,16 +3481,132 @@
 eturned.
+ However, this%0A     * method attempts to find the combination with the smallest number of%0A     * %7B@link Challenge%7Ds.
 %0A     *%0A
@@ -3705,24 +3705,148 @@
 ular order.%0A
+     *            Basically this is a collection of all challenge types supported by your%0A     *            implementation.%0A
      * @retu
@@ -3959,34 +3959,30 @@
 ort 
-this challenge combination
+any of your challenges
 . Th
@@ -4006,16 +4006,22 @@
 returned
+ in no
 %0A     * 
@@ -4028,22 +4028,16 @@
         
-in no 
 particul
@@ -4036,32 +4036,192 @@
 particular order
+. The result may be a subset of the types you have%0A     *         provided, if fewer challenges are actually required for a successful%0A     *         validation
 .%0A     */%0A    pu
@@ -4309,24 +4309,24 @@
 String%3E 
-referenc
+availabl
 e = Arra
@@ -4342,16 +4342,127 @@
 (types);
+%0A        Collection%3CString%3E combinationTypes = new ArrayList%3C%3E();%0A%0A        Collection%3CChallenge%3E result = null;
 %0A%0A      
@@ -4530,61 +4530,30 @@
     
-Collection%3CString%3E combinationTypes = new ArrayList%3C%3E
+combinationTypes.clear
 ();%0A
@@ -4684,28 +4684,30 @@
 if (
-reference.size() == 
+available.containsAll(
 comb
@@ -4722,15 +4722,12 @@
 ypes
-.size()
+) &&
 %0A   
@@ -4747,33 +4747,43 @@
     
-&& reference.containsAll(
+(result == null %7C%7C result.size() %3E 
 comb
@@ -4789,21 +4789,23 @@
 bination
-Types
+.size()
 )) %7B%0A   
@@ -4811,36 +4811,38 @@
               re
-turn
+sult =
  combination;%0A  
@@ -4879,20 +4879,22 @@
  return 
-null
+result
 ;%0A    %7D%0A

Extend findCombination behavior.