package org.jgroups.tests;

import org.jgroups.util.Util;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.*;

/**
 * Discovers all UDP-based members running on a certain mcast address
 * @author Bela Ban
 * @version $Revision: 1.18 $
 * Date: Jun 2, 2003
 * Time: 4:35:29 PM
 */
public class Probe {
    MulticastSocket mcast_sock;
    volatile boolean running=true;
    final Set<String> senders=new HashSet<String>();


    public Probe() {

    }

    public void start(InetAddress addr, InetAddress bind_addr, int port, int ttl,
                      final long timeout, List query, String match, boolean weed_out_duplicates) throws Exception {
        mcast_sock=new MulticastSocket();
        mcast_sock.setTimeToLive(ttl);
        if(bind_addr != null)
            mcast_sock.setInterface(bind_addr);

        StringBuilder request=new StringBuilder();
        for(int i=0; i < query.size(); i++) {
            request.append(query.get(i)).append(" ");
        }
        byte[] probe_buf=request.toString().getBytes();

        DatagramPacket probe=new DatagramPacket(probe_buf, 0, probe_buf.length, addr, port);
        mcast_sock.send(probe);
        System.out.println("\n-- send probe on " + addr + ':' + port + '\n');


        new Thread() {
            public void run() {
                Util.sleep(timeout);
                mcast_sock.close();
                running=false;
            }
        }.start();

        int matched=0, not_matched=0, count=0;
        String response;
        while(running) {
            byte[] buf=new byte[65000];
            DatagramPacket rsp=new DatagramPacket(buf, 0, buf.length);
            try {
                mcast_sock.receive(rsp);
            }
            catch(Throwable t) {
                System.out.println("\n");
                return;
            }

            byte[] data=rsp.getData();
            response=new String(data, 0, rsp.getLength());
            if(weed_out_duplicates && checkDuplicateResponse(response)) {
                continue;
            }

            if(matches(response, match)) {
                matched++;
                System.out.println("\n#" + ++count + " (" + rsp.getLength() + " bytes):\n" + response);
            }
            else
                not_matched++;
        }
        System.out.println("\nTotal responses=" + count + ", " + matched + " matches, " + not_matched + " non-matches");
    }

    private boolean checkDuplicateResponse(String response) {
        int index=response.indexOf("local_addr");
        if(index != -1) {
            String addr=parseAddress(response.substring(index+1 + "local_addr".length()));
            return senders.add(addr) == false;
        }

        return false;
    }

    private static String parseAddress(String response) {
        StringTokenizer st=new StringTokenizer(response);
        return st.nextToken();
    }

    private static boolean matches(String response, String match) {
        if(response == null)
            return false;
        if(match == null)
            return true;
        int index=response.indexOf(match);
        return index > -1;
    }


    public static void main(String[] args) {
        InetAddress  addr=null, bind_addr=null;
        int          port=0;
        int          ttl=32;
        long         timeout=500;
        final String DEFAULT_DIAG_ADDR="224.0.75.75";
        final String DEFAULT_DIAG_ADDR_IPv6="ff0e::0:75:75";
        final int    DEFAULT_DIAG_PORT=7500;
        List<String> query=new ArrayList<String>();
        String       match=null;
        boolean      weed_out_duplicates=false;
        boolean      ipv6=false;

        try {
            for(int i=0; i < args.length; i++) {
                if("-addr".equals(args[i])) {
                    addr=InetAddress.getByName(args[++i]);
                    continue;
                }
                if("-bind_addr".equals(args[i])) {
                    bind_addr=InetAddress.getByName(args[++i]);
                    continue;
                }
                if("-port".equals(args[i])) {
                    port=Integer.parseInt(args[++i]);
                    continue;
                }
                if("-ttl".equals(args[i])) {
                    ttl=Integer.parseInt(args[++i]);
                    continue;
                }
                if("-timeout".equals(args[i])) {
                    timeout=Long.parseLong(args[++i]);
                    continue;
                }
                if("-match".equals(args[i])) {
                    match=args[++i];
                    continue;
                }
                if("-weed_out_duplicates".equals(args[i])) {
                    weed_out_duplicates=true;
                    continue;
                }
                if("-ipv6".equals(args[i])) {
                    ipv6=true;
                    continue;
                }
                if("-help".equals(args[i]) || "-h".equals(args[i])) {
                    help();
                    return;
                }
                query.add(args[i]);
            }
            Probe p=new Probe();
            if(addr == null)
                addr=ipv6? InetAddress.getByName(DEFAULT_DIAG_ADDR_IPv6) : InetAddress.getByName(DEFAULT_DIAG_ADDR);
            if(port == 0)
                port=DEFAULT_DIAG_PORT;
            p.start(addr, bind_addr, port, ttl, timeout, query, match, weed_out_duplicates);
        }
        catch(Throwable t) {
            t.printStackTrace();
        }
    }

    static void help() {
        System.out.println("Probe [-help] [-addr <addr>] [-ipv6] [-bind_addr <addr>] " +
                "[-port <port>] [-ttl <ttl>] [-timeout <timeout>] [-weed_out_duplicates] " +
                "[-match <pattern>] QUERY\n" +
                "(QUERY is a whitespace separate list of keys)");
    }
}

@@ -291,9 +291,9 @@
  1.1
-8
+9
  $%0A 
@@ -1846,38 +1846,37 @@
                 
+b
 re
-turn
+ak
 ;%0A            %7D%0A
@@ -2081,32 +2081,53 @@
 %0A            %7D%0A%0A
+            count++;%0A
             if(m
@@ -2223,18 +2223,16 @@
 %22%5Cn#%22 + 
-++
 count + 
@@ -2388,24 +2388,8 @@
 (%22%5Cn
-Total responses=
 %22 + 
@@ -2397,18 +2397,28 @@
 ount + %22
-,
  
+responses (
 %22 + matc
@@ -2461,16 +2461,17 @@
  non
--
+ 
 matches
+)
 %22);%0A
@@ -5772,16 +5772,32 @@
 icates%5D 
+%5B-match pattern%5D
 %22 +%0A    
@@ -5814,99 +5814,313 @@
   %22%5B
--match %3Cpattern%3E%5D QUERY%5Cn%22 +%0A                %22(QUERY is a whitespace separate list of keys)
+key%5B=value%5D%5D*%5Cn%5Cn%22 +%0A                %22Examples:%5Cn%22 +%0A                %22probe.sh keys // dumps all keys%5Cn%22 +%0A                %22probe.sh jmx=NAKACK // dumps JMX info about all NAKACK protocols%5Cn%22 +%0A                %22probe.sh op=STABLE.runMessageGarbageCollection // invokes the method in all STABLE protocols%5Cn
 %22);%0A

correct showing of matches / non-matches (https://jira.jboss.org/jira/browse/JGRP-1184)