/*
 * Decompiled with CFR 0.152.
 */
package org.jivesoftware.smack.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.util.StringTransformer;
import org.jivesoftware.smack.util.dns.DNSResolver;
import org.jivesoftware.smack.util.dns.HostAddress;
import org.jivesoftware.smack.util.dns.SRVRecord;

public class DNSUtil {
    private static final Logger LOGGER = Logger.getLogger(DNSUtil.class.getName());
    private static DNSResolver dnsResolver = null;
    private static StringTransformer idnaTransformer = new StringTransformer(){

        @Override
        public String transform(String string) {
            return string;
        }
    };

    public static void setDNSResolver(DNSResolver resolver) {
        dnsResolver = resolver;
    }

    public static DNSResolver getDNSResolver() {
        return dnsResolver;
    }

    public static void setIdnaTransformer(StringTransformer idnaTransformer) {
        if (idnaTransformer == null) {
            throw new NullPointerException();
        }
        DNSUtil.idnaTransformer = idnaTransformer;
    }

    public static List<HostAddress> resolveXMPPDomain(String domain, List<HostAddress> failedAddresses) {
        domain = idnaTransformer.transform(domain);
        if (dnsResolver == null) {
            LOGGER.warning("No DNS Resolver active in Smack, will be unable to perform DNS SRV lookups");
            ArrayList<HostAddress> addresses = new ArrayList<HostAddress>(1);
            addresses.add(new HostAddress(domain, 5222));
            return addresses;
        }
        return DNSUtil.resolveDomain(domain, DomainType.Client, failedAddresses);
    }

    public static List<HostAddress> resolveXMPPServerDomain(String domain, List<HostAddress> failedAddresses) {
        domain = idnaTransformer.transform(domain);
        if (dnsResolver == null) {
            LOGGER.warning("No DNS Resolver active in Smack, will be unable to perform DNS SRV lookups");
            ArrayList<HostAddress> addresses = new ArrayList<HostAddress>(1);
            addresses.add(new HostAddress(domain, 5269));
            return addresses;
        }
        return DNSUtil.resolveDomain(domain, DomainType.Server, failedAddresses);
    }

    private static List<HostAddress> resolveDomain(String domain, DomainType domainType, List<HostAddress> failedAddresses) {
        ArrayList<HostAddress> addresses;
        block8: {
            String srvDomain;
            addresses = new ArrayList<HostAddress>();
            switch (domainType) {
                case Server: {
                    srvDomain = "_xmpp-server._tcp." + domain;
                    break;
                }
                case Client: {
                    srvDomain = "_xmpp-client._tcp." + domain;
                    break;
                }
                default: {
                    throw new AssertionError();
                }
            }
            try {
                List<SRVRecord> srvRecords = dnsResolver.lookupSRVRecords(srvDomain);
                if (LOGGER.isLoggable(Level.FINE)) {
                    String logMessage = "Resolved SRV RR for " + srvDomain + ":";
                    for (SRVRecord r : srvRecords) {
                        logMessage = logMessage + " " + r;
                    }
                    LOGGER.fine(logMessage);
                }
                List<HostAddress> sortedRecords = DNSUtil.sortSRVRecords(srvRecords);
                addresses.addAll(sortedRecords);
            }
            catch (Exception e) {
                LOGGER.log(Level.WARNING, "Exception while resovling SRV records for " + domain + ". Consider adding '_xmpp-(server|client)._tcp' DNS SRV Records", e);
                if (failedAddresses == null) break block8;
                HostAddress failedHostAddress = new HostAddress(srvDomain);
                failedHostAddress.setException(e);
                failedAddresses.add(failedHostAddress);
            }
        }
        addresses.add(new HostAddress(domain));
        return addresses;
    }

    private static List<HostAddress> sortSRVRecords(List<SRVRecord> records) {
        List bucket;
        if (records.size() == 1 && records.get(0).getFQDN().equals(".")) {
            return Collections.emptyList();
        }
        Collections.sort(records);
        TreeMap<Integer, LinkedList<SRVRecord>> buckets = new TreeMap<Integer, LinkedList<SRVRecord>>();
        for (SRVRecord r : records) {
            Integer priority = r.getPriority();
            bucket = (LinkedList<SRVRecord>)buckets.get(priority);
            if (bucket == null) {
                bucket = new LinkedList<SRVRecord>();
                buckets.put(priority, (LinkedList<SRVRecord>)bucket);
            }
            bucket.add(r);
        }
        ArrayList<HostAddress> res = new ArrayList<HostAddress>(records.size());
        for (Integer priority : buckets.keySet()) {
            int bucketSize;
            bucket = (List)buckets.get(priority);
            while ((bucketSize = bucket.size()) > 0) {
                int selectedPos;
                int[] totals = new int[bucket.size()];
                int running_total = 0;
                int count = 0;
                int zeroWeight = 1;
                for (SRVRecord r : bucket) {
                    if (r.getWeight() <= 0) continue;
                    zeroWeight = 0;
                }
                for (SRVRecord r : bucket) {
                    totals[count] = running_total += r.getWeight() + zeroWeight;
                    ++count;
                }
                if (running_total == 0) {
                    selectedPos = (int)(Math.random() * (double)bucketSize);
                } else {
                    double rnd = Math.random() * (double)running_total;
                    selectedPos = DNSUtil.bisect(totals, rnd);
                }
                SRVRecord chosenSRVRecord = (SRVRecord)bucket.remove(selectedPos);
                res.add(chosenSRVRecord);
            }
        }
        return res;
    }

    private static int bisect(int[] array, double value) {
        int pos = 0;
        for (int element : array) {
            if (value < (double)element) break;
            ++pos;
        }
        return pos;
    }

    private static enum DomainType {
        Server,
        Client;

    }
}
