package com.qiniu.android.dns.dns;

import com.qiniu.android.dns.Domain;
import com.qiniu.android.dns.IResolver;
import com.qiniu.android.dns.NetworkInfo;
import com.qiniu.android.dns.Record;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public abstract class DnsResolver implements IResolver {

    private static ScheduledExecutorService timeoutExecutorService = Executors.newSingleThreadScheduledExecutor();

    private final int recordType;
    private final String[] servers;
    private final ExecutorService executorService;

    protected final int timeout;

    public DnsResolver(String server) {
        this(server, Record.TYPE_A, DNS_DEFAULT_TIMEOUT);
    }

    public DnsResolver(String server, int timeout) {
        this(server, Record.TYPE_A, timeout);
    }

    public DnsResolver(String server, int recordType, int timeout) {
        this(server == null ? null : new String[]{server}, recordType, timeout, null);
    }

    public DnsResolver(String[] servers, int recordType, int timeout) {
        this(servers, recordType, timeout, (servers != null && servers.length > 0) ? Executors.newFixedThreadPool(servers.length) : null);
    }

    public DnsResolver(String[] servers, int recordType, int timeout, ExecutorService executorService) {
        this.recordType = recordType;
        this.timeout = timeout > 0 ? timeout : DNS_DEFAULT_TIMEOUT;
        this.servers = servers;
        this.executorService = executorService;
    }

    @Override
    public Record[] resolve(Domain domain, NetworkInfo info) throws IOException {
        DnsResponse response = lookupHost(domain.domain);
        if (response == null) {
            throw new IOException("response is null");
        }

        List<Record> answers = response.getAnswerArray();
        if (answers == null || answers.size() == 0) {
            return null;
        }

        List<Record> records = new ArrayList<>();
        for (Record record : answers) {
            if (record.isA() || record.isCname() || record.isAAAA()) {
                records.add(record);
            }
        }
        return records.toArray(new Record[0]);
    }

    private DnsResponse lookupHost(String host) throws IOException {
        return request(host, recordType);
    }

    private DnsResponse request(final String host, final int recordType) throws IOException {
        if (servers == null || servers.length == 0) {
            throw new IOException("server can not empty");
        }

        if (host == null || host.length() == 0) {
            throw new IOException("host can not empty");
        }

        if (servers.length == 1 || executorService == null) {
            DnsResponse response = null;
            for (String server : servers) {
                response = request(server, host, recordType);
                if (response != null) {
                    break;
                }
            }
            return response;
        } else {
            final DnsResponse[] response = {null};
            final int[] completedCount = {0};
            final Object waiter = new Object();

            // 超时处理
            timeoutExecutorService.schedule(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    synchronized (waiter) {
                        waiter.notify();
                    }
                    return null;
                }
            }, timeout, TimeUnit.SECONDS);

            // 返回一个即结束
            for (final String server : servers) {
                final String serverP = server;
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (waiter) {
                            try {
                                response[0] = request(serverP, host, recordType);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            completedCount[0] += 1;

                            if (completedCount[0] == servers.length || response[0] != null) {
                                waiter.notify();
                            }
                        }
                    }
                });
            }

            synchronized (waiter) {
                try {
                    waiter.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return response[0];
        }

    }

     abstract DnsResponse request(String server, String host, int recordType) throws IOException;
}

@@ -3235,16 +3235,69 @@
 %7Bnull%7D;%0A
+            final IOException%5B%5D exceptions = %7Bnull%7D;%0A
         
@@ -3334,16 +3334,16 @@
  = %7B0%7D;%0A
-
         
@@ -3631,32 +3631,160 @@
 aiter.notify();%0A
+                        exceptions%5B0%5D = new IOException(%22resolver timeout for server:%22 + servers.toString() + %22 host:%22 + host);%0A
                 
@@ -4393,32 +4393,83 @@
 ntStackTrace();%0A
+                                exceptions%5B0%5D = e;%0A
                 
@@ -4986,32 +4986,32 @@
               %7D%0A
-
             %7D%0A  
@@ -5000,32 +5000,127 @@
 %7D%0A            %7D%0A
+%0A            if (exceptions%5B0%5D != null) %7B%0A                throw  exceptions%5B0%5D;%0A            %7D%0A%0A
             retu

optimize dns resolver timeout desc