package org.jivesoftware.smack.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
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() {
      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();
      } else {
         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");
         List<HostAddress> addresses = new ArrayList(1);
         addresses.add(new HostAddress(domain, 5222));
         return addresses;
      } else {
         return resolveDomain(domain, DNSUtil.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");
         List<HostAddress> addresses = new ArrayList(1);
         addresses.add(new HostAddress(domain, 5269));
         return addresses;
      } else {
         return resolveDomain(domain, DNSUtil.DomainType.Server, failedAddresses);
      }
   }

   private static List<HostAddress> resolveDomain(String domain, DNSUtil.DomainType domainType, List<HostAddress> failedAddresses) {
      List<HostAddress> addresses = new ArrayList();
      String srvDomain;
      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 + ":";

            SRVRecord r;
            for(Iterator i$ = srvRecords.iterator(); i$.hasNext(); logMessage = logMessage + " " + r) {
               r = (SRVRecord)i$.next();
            }

            LOGGER.fine(logMessage);
         }

         List<HostAddress> sortedRecords = sortSRVRecords(srvRecords);
         addresses.addAll(sortedRecords);
      } catch (Exception var9) {
         LOGGER.log(Level.WARNING, "Exception while resovling SRV records for " + domain + ". Consider adding '_xmpp-(server|client)._tcp' DNS SRV Records", var9);
         if (failedAddresses != null) {
            HostAddress failedHostAddress = new HostAddress(srvDomain);
            failedHostAddress.setException(var9);
            failedAddresses.add(failedHostAddress);
         }
      }

      addresses.add(new HostAddress(domain));
      return addresses;
   }

   private static List<HostAddress> sortSRVRecords(List<SRVRecord> records) {
      if (records.size() == 1 && ((SRVRecord)records.get(0)).getFQDN().equals(".")) {
         return Collections.emptyList();
      } else {
         Collections.sort(records);
         SortedMap<Integer, List<SRVRecord>> buckets = new TreeMap();

         SRVRecord r;
         Integer priority;
         Object bucket;
         for(Iterator i$ = records.iterator(); i$.hasNext(); ((List)bucket).add(r)) {
            r = (SRVRecord)i$.next();
            priority = r.getPriority();
            bucket = (List)buckets.get(priority);
            if (bucket == null) {
               bucket = new LinkedList();
               buckets.put(priority, bucket);
            }
         }

         List<HostAddress> res = new ArrayList(records.size());
         Iterator i$ = buckets.keySet().iterator();

         while(i$.hasNext()) {
            priority = (Integer)i$.next();
            List bucket = (List)buckets.get(priority);

            int bucketSize;
            while((bucketSize = bucket.size()) > 0) {
               int[] totals = new int[bucket.size()];
               int running_total = 0;
               int count = 0;
               int zeroWeight = 1;
               Iterator i$ = bucket.iterator();

               SRVRecord chosenSRVRecord;
               while(i$.hasNext()) {
                  chosenSRVRecord = (SRVRecord)i$.next();
                  if (chosenSRVRecord.getWeight() > 0) {
                     zeroWeight = 0;
                  }
               }

               for(i$ = bucket.iterator(); i$.hasNext(); ++count) {
                  chosenSRVRecord = (SRVRecord)i$.next();
                  running_total += chosenSRVRecord.getWeight() + zeroWeight;
                  totals[count] = running_total;
               }

               int selectedPos;
               if (running_total == 0) {
                  selectedPos = (int)(Math.random() * (double)bucketSize);
               } else {
                  double rnd = Math.random() * (double)running_total;
                  selectedPos = bisect(totals, rnd);
               }

               chosenSRVRecord = (SRVRecord)bucket.remove(selectedPos);
               res.add(chosenSRVRecord);
            }
         }

         return res;
      }
   }

   private static int bisect(int[] array, double value) {
      int pos = 0;
      int[] arr$ = array;
      int len$ = array.length;

      for(int i$ = 0; i$ < len$; ++i$) {
         int element = arr$[i$];
         if (value < (double)element) {
            break;
         }

         ++pos;
      }

      return pos;
   }

   private static enum DomainType {
      Server,
      Client;
   }
}
