
package com.sun.xml.ws.commons.virtualbox;

import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.WebServiceException;
import org.virtualbox.service.InvalidObjectFaultMsg;
import org.virtualbox.service.RuntimeFaultMsg;
import org.virtualbox.service.VboxService;
import org.virtualbox.service.VboxPortType;
import java.util.Map;
import java.util.HashMap;

class PortPool
{
    private VboxService svc;
    private Map<VboxPortType, Integer> known;
    private boolean initStarted;

    PortPool(boolean usePreinit)
    {
        known = new HashMap<VboxPortType, Integer>();

        if (usePreinit)
        {
           new Thread(new Runnable()
              {
                 public void run()
                 {
                    // need to sync on something else but 'this'
                    synchronized (known)
                    {
                      initStarted = true;
                      known.notify();
                    }

                    preinit();
                 }
               }).start();

           synchronized (known)
           {
              while (!initStarted)
              { 
                 try {
                   known.wait();
                 } catch (InterruptedException e) {
                 break;
                 }
              }
           }
        }
    }

    private synchronized void preinit()
    {
        VboxPortType port = getPort();
        releasePort(port);
    }

    synchronized VboxPortType getPort()
    {
        VboxPortType port = null;
        for (VboxPortType cur: known.keySet())
        {
            if (known.get(cur) == 0)
            {
                port = cur;
                break;
            }
        }

        if (port == null)
        {
            if (svc == null) 
            {
                URL wsdl = PortPool.class.getClassLoader().getResource("vboxwebService.wsdl");
                if (wsdl == null)
                    throw new LinkageError("vboxwebService.wsdl not found, but it should have been in the jar");
                svc = new VboxService(wsdl, 
                                      new QName("http://www.virtualbox.org/Service", 
                                                "vboxService"));
            }
            port = svc.getVboxServicePort();
            known.put(port, new Integer(1));
        }
        return port;
    }

    synchronized void releasePort(VboxPortType port)
    {
        Integer val =  known.get(port);
        if (val == null || val == 0)
        {
            // know you not
            return;
        }
        known.put(port, val - 1);
    }
}

public class VirtualBox {
    private static PortPool pool = new PortPool(true);

    public static IVirtualBox connect(URL url) 
    {
        return connect(url.toExternalForm());
    }
    
    public static IVirtualBox connect(String url) 
    {
        return connect(url, "", "");
    }

    public static IVirtualBox connect(String url, String user, String password) {
        try {
            VboxPortType port = pool.getPort();
            ((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
            String vbox = port.iWebsessionManagerLogon(user, password);
            return new IVirtualBox(vbox, port);
        } catch (InvalidObjectFaultMsg e) {
          throw new WebServiceException(e);
        } catch (RuntimeFaultMsg e) {
          throw new WebServiceException(e);
        }
    }

    public static void disconnect(IVirtualBox vbox)
    {
        try {
           VboxPortType port = vbox.getRemoteWSPort();
           port.iWebsessionManagerLogoff(vbox.getRef());
           pool.releasePort(port);
        } catch (InvalidObjectFaultMsg e) {
          throw new WebServiceException(e);
        } catch (RuntimeFaultMsg e) {
          throw new WebServiceException(e);
        }
    }

}
