/**************************************************************************************
 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
 * http://aspectwerkz.codehaus.org                                                    *
 * ---------------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the LGPL license      *
 * a copy of which has been included with this distribution in the license.txt file.  *
 **************************************************************************************/
package org.codehaus.aspectwerkz.hook;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * Starts a target process adding JDWP option to have a listening connector and be in suspend mode <p/>Target process is
 * launched using <i>$JAVA_HOME/bin/java [opt] [main] </i> <br/>and [opt] is patched to use <i>-Xdebug
 * -Xrunjdwp:transport=..,address=..,server=y,suspend=y </i> <br/>
 * 
 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
 */
public class JDWPStarter extends AbstractStarter {
    private String transport;

    private String address;

    public JDWPStarter(String opt, String main, String transport, String address) {
        super(opt, main);
        Map jdwpOpt = parseJdwp();
        if (jdwpOpt.containsKey("transport")) {
            this.transport = (String) jdwpOpt.get("transport");
        } else {
            this.transport = transport;
            jdwpOpt.put("transport", this.transport);
        }
        if (jdwpOpt.containsKey("address")) {
            this.address = (String) jdwpOpt.get("address");
        } else {
            this.address = address;
            jdwpOpt.put("address", this.address);
        }
        patchOptions(jdwpOpt);
    }

    public String getTransport() {
        return transport;
    }

    public String getAddress() {
        return address;
    }

    /**
     * Patch JDWP options if any to include necessary information Preserve JDWP options excepted server and suspend.
     * <br/>If transport and address are already specified it uses them.
     */
    private void patchOptions(Map jdwpOpt) {
        if (opt.indexOf("-Xdebug") < 0) {
            opt = "-Xdebug " + opt;
        }
        jdwpOpt.put("server", "y");
        jdwpOpt.put("suspend", "y");
        StringBuffer jdwp = new StringBuffer("-Xrunjdwp:");
        List keys = new ArrayList(jdwpOpt.keySet());

        // JDWP options should start with transport=..,address=..
        // or it fails strangely
        //Collections.reverse(keys);
        keys = jdwpOptionSort(keys);
        for (Iterator i = keys.iterator(); i.hasNext();) {
            String key = (String) i.next();
            jdwp.append(key).append("=").append((String) jdwpOpt.get(key));
            if (i.hasNext()) {
                jdwp.append(",");
            }
        }
        if (opt.indexOf("-Xrunjdwp:") < 0) {
            opt = jdwp + " " + opt;
        } else {
            int from = opt.indexOf("-Xrunjdwp:");
            int to = Math.min(opt.length(), opt.indexOf(' ', from));
            StringBuffer newOpt = new StringBuffer("");
            if (from > 0) {
                newOpt.append(opt.substring(0, from));
            }
            newOpt.append(" ").append(jdwp);
            if (to < opt.length()) {
                newOpt.append(" ").append(opt.substring(to, opt.length()));
            }
            opt = newOpt.toString();
        }
    }

    /**
     * return a Map(String=>String) of JDWP options
     */
    private Map parseJdwp() {
        if (opt.indexOf("-Xrunjdwp:") < 0) {
            return new HashMap();
        }
        String jdwp = opt.substring(opt.indexOf("-Xrunjdwp:") + "-Xrunjdwp:".length(), Math.min(opt.length(), opt
                .indexOf(' ', opt.indexOf("-Xrunjdwp:"))));
        HashMap jdwpOpt = new HashMap();
        StringTokenizer stz = new StringTokenizer(jdwp, ",");
        while (stz.hasMoreTokens()) {
            String jdwpo = stz.nextToken();
            if (jdwpo.indexOf('=') < 0) {
                System.err.println("WARN - unrecognized JDWP option: " + jdwpo);
                continue;
            }
            jdwpOpt.put(jdwpo.substring(0, jdwpo.indexOf('=')), jdwpo.substring(jdwpo.indexOf('=') + 1));
        }
        return jdwpOpt;
    }

    /**
     * Sort list of String for "transport" to be in first position
     */
    private List jdwpOptionSort(List opt) {
        Comparator c = new Comparator() {
            public int compare(Object o1, Object o2) {
                if (o1 instanceof String && o2 instanceof String) {
                    if ("transport".equals((String) o1)) {
                        return -1000;
                    }
                    if ("transport".equals((String) o2)) {
                        return 1000;
                    }
                    return 0;
                }
                return 0;
            }
        };
        Collections.sort(opt, c);
        return opt;
    }
}