package fr.masciulli.udpdiscoverer.lib;

import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.WifiManager;
import android.support.annotation.Nullable;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Discoverer {

    private final Context context;
    private int localPort = -1;
    private int remotePort = -1;
    @Nullable
    private Callback callback;
    private byte[] data;

    public Discoverer(Context context) {
        this.context = context;
    }

    public static Discoverer from(Context context) {
        return new Discoverer(context);
    }

    public Discoverer remotePort(int port) {
        this.remotePort = port;
        return this;
    }

    public Discoverer localPort(int port) {
        this.localPort = port;
        return this;
    }

    public Discoverer data(byte[] data) {
        this.data = data;
        return this;
    }

    public Discoverer callback(Callback callback) {
        this.callback = callback;
        return this;
    }

    public void broadcast() {
        if (localPort == -1) {
            error(new IllegalArgumentException("Local port not set"));
            return;
        }

        if (remotePort == -1) {
            error(new IllegalArgumentException("Remote port not set"));
            return;
        }

        if (data == null) {
            error(new IllegalArgumentException("No data to send"));
            return;
        }

        try {
            InetAddress address = getBroadcastAddress();
            final DatagramSocket socket = new DatagramSocket(localPort);
            socket.setBroadcast(true);

            Callback broadcastCallback = new Callback() {
                @Override
                public void error(Exception exception) {
                    if (callback != null) {
                        callback.error(exception);
                    }
                }

                @Override
                public void messageSent() {
                    if (callback != null) {
                        callback.messageSent();
                    }
                    new ListenTask(socket, callback).execute();
                }

                @Override
                public void responseReceived(DatagramPacket response) {
                    if (callback != null) {
                        callback.responseReceived(response);
                    }
                }
            };

            new BroadcastTask(socket, address, remotePort, broadcastCallback).execute(data);
        } catch (IOException exception) {
            error(exception);
        }
    }

    private InetAddress getBroadcastAddress() throws IOException {
        WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        DhcpInfo dhcp = wifi.getDhcpInfo();
        if (dhcp == null) {
            throw new IOException("Could not retrieve DHCP info");
        }

        int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
        byte[] quads = new byte[4];
        for (int k = 0; k < 4; k++) {
            quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
        }
        return InetAddress.getByAddress(quads);
    }

    private void error(Exception exception) {
        if (callback != null) {
            callback.error(exception);
        }
    }

}

@@ -326,16 +326,165 @@
 erer %7B%0A%0A
+    private static final int STATE_IDLE = 0;%0A    private static final int STATE_BROADCASTING = 0;%0A    private static final int STATE_LISTENING = 0;%0A%0A
     priv
@@ -644,16 +644,59 @@
 %5B%5D data;
+%0A    private int currentState = STATE_IDLE;
 %0A%0A    pu
@@ -2061,104 +2061,41 @@
     
-if (callback != null) %7B%0A                        callback.error(exception);%0A                    %7D
+Discoverer.this.error(exception);
 %0A   
@@ -2204,64 +2204,23 @@
     
-if (callback != null) %7B%0A                        callback
+Discoverer.this
 .mes
@@ -2243,33 +2243,63 @@
                 
-%7D
+currentState = STATE_LISTENING;
 %0A               
@@ -2488,64 +2488,23 @@
     
-if (callback != null) %7B%0A                        callback
+Discoverer.this
 .res
@@ -2536,36 +2536,32 @@
                 
-   
- 
 %7D%0A              
@@ -2550,37 +2550,35 @@
   %7D%0A            
-    %7D
+%7D;%0A
 %0A            %7D;%0A
@@ -2566,35 +2566,66 @@
 %7D;%0A%0A            
-%7D;%0A
+currentState = STATE_BROADCASTING;
 %0A            new
@@ -2783,32 +2783,298 @@
        %7D%0A    %7D%0A%0A
+    public boolean isIdle() %7B%0A        return currentState == STATE_IDLE;%0A    %7D%0A%0A    public boolean isBroadcasting() %7B%0A        return currentState == STATE_BROADCASTING;%0A    %7D%0A%0A    public boolean isListening() %7B%0A        return currentState == STATE_LISTENING;%0A    %7D%0A%0A
     private Inet
@@ -3767,13 +3767,324 @@
   %7D%0A    
+    currentState = STATE_IDLE;%0A    %7D%0A%0A    private void messageSent() %7B%0A        if (callback != null) %7B%0A            callback.messageSent();%0A        %7D%0A    %7D%0A%0A    private void responseReceived(DatagramPacket response) %7B%0A        if (callback != null) %7B%0A            callback.responseReceived(response);%0A        %7D%0A    
 %7D
-%0A
 %0A%7D%0A

Introduce state variable