package net.hyperic.sigar.cmd;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

import net.hyperic.sigar.NetServices;
import net.hyperic.sigar.SigarException;
import net.hyperic.sigar.NetConnection;
import net.hyperic.sigar.NetFlags;

/**
 * Display network connections.
 */
public class Netstat extends SigarCommandBase {

    private static final int LADDR_LEN = 20;
    private static final int RADDR_LEN = 35;

    private static final String[] HEADER = new String[] {
        "Proto",
        "Local Address",
        "Foreign Address",
        "State"
    };

    private static boolean isNumeric;

    public Netstat(Shell shell) {
        super(shell);
    }

    public Netstat() {
        super();
    }

    protected boolean validateArgs(String[] args) {
        return true;
    }

    public String getUsageShort() {
        return "Display network connections";
    }

    //poor mans getopt.
    public static int getFlags(String[] args, int flags) {
        int proto_flags = 0;
        isNumeric = false;

        for (int i=0; i<args.length; i++) {
            String arg = args[i];
            int j = 0;

            while (j<arg.length()) {
                switch (arg.charAt(j++)) {
                  case '-':
                    continue;
                  case 'l':
                    flags &= ~NetFlags.CONN_CLIENT;
                    flags |= NetFlags.CONN_SERVER;
                    break;
                  case 'a':
                    flags |= NetFlags.CONN_SERVER | NetFlags.CONN_CLIENT;
                    break;
                  case 'n':
                    isNumeric = true;
                    break;
                  case 't':
                    proto_flags |= NetFlags.CONN_TCP;
                    break;
                  case 'u':
                    proto_flags |= NetFlags.CONN_UDP;
                    break;
                  case 'w':
                    proto_flags |= NetFlags.CONN_RAW;
                    break;
                  case 'x':
                    proto_flags |= NetFlags.CONN_UNIX;
                    break;
                  default:
                    System.err.println("unknown option");
                }
            }
        }

        if (proto_flags != 0) {
            flags &= ~NetFlags.CONN_PROTOCOLS;
            flags |= proto_flags;
        }

        return flags;
    }

    private String formatPort(String proto, long port) {
        if (port == 0) {
            return "*";
        }
        if (!isNumeric) {
            String service = NetServices.getName(proto, port);
            if (service != null) {
                return service;
            }
        }
        return String.valueOf(port);
    }

    private String formatAddress(String proto, String ip,
                                 long portnum, int max) {
        
        String port = formatPort(proto, portnum);
        String address;

        if (NetFlags.isAnyAddress(ip)) {
            address = "*";
        }
        else if (isNumeric) {
            address = ip;
        }
        else {
            try {
                address = InetAddress.getByName(ip).getHostName();
            } catch (UnknownHostException e) {
                address = ip;
            }
        }

        max -= port.length() + 1;
        if (address.length() > max) {
            address = address.substring(0, max);
        }

        return address + ":" + port; 
    }

    //XXX currently weak sauce.  should end up like netstat command.
    public void output(String[] args) throws SigarException {
        //default
        int flags = NetFlags.CONN_CLIENT | NetFlags.CONN_PROTOCOLS;

        if (args.length > 0) {
            flags = getFlags(args, flags);
        }

        NetConnection[] connections = this.sigar.getNetConnectionList(flags);
        printf(HEADER);

        for (int i=0; i<connections.length; i++) {
            NetConnection conn = connections[i];
            String proto = conn.getTypeString();
            String state;

            if (conn.getType() == NetFlags.CONN_UDP) {
                state = "";
            }
            else {
                state = conn.getStateString();
            }

            ArrayList items = new ArrayList();
            items.add(proto);
            items.add(formatAddress(proto,
                                    conn.getLocalAddress(),
                                    conn.getLocalPort(),
                                    LADDR_LEN));
            items.add(formatAddress(proto,
                                    conn.getRemoteAddress(),
                                    conn.getRemotePort(),
                                    RADDR_LEN));
            items.add(state);
            printf(items);
        }
    }

    public static void main(String[] args) throws Exception {
        new Netstat().processCommand(args);
    }
}

@@ -598,16 +598,28 @@
  %22State%22
+,%0A        %22%22
 %0A    %7D;%0A
@@ -655,16 +655,25 @@
 sNumeric
+, wantPid
 ;%0A%0A    p
@@ -1084,16 +1084,41 @@
 = false;
+%0A        wantPid = false;
 %0A%0A      
@@ -1723,32 +1723,123 @@
          break;%0A
+                  case 'p':%0A                    wantPid = true;%0A                    break;%0A
                 
@@ -4890,16 +4890,810 @@
 state);%0A
+%0A            String process = null;%0A            if (wantPid &&%0A                //XXX only works w/ listen ports%0A                (conn.getState() == NetFlags.TCP_LISTEN))%0A            %7B%0A                try %7B%0A                    long pid =%0A                        this.sigar.getProcPort(conn.getType(),%0A                                               conn.getLocalPort());%0A                    if (pid != 0) %7B //XXX another bug%0A                        String name =%0A                            this.sigar.getProcState(pid).getName();%0A                        process = pid + %22/%22 + name;%0A                    %7D%0A                %7D catch (SigarException e) %7B%0A                %7D%0A            %7D%0A%0A            if (process == null) %7B%0A                process = %22%22;%0A            %7D%0A%0A            items.add(process);%0A%0A
         

add support for pid flag