package net.java.sip.communicator.impl.netaddr;

import net.java.stun4j.*;
import net.java.stun4j.message.*;
import net.java.stun4j.stack.*;

/**
 * A utility used to flatten the multithreaded architecture of the Stack
 * and execute the discovery process in a synchronized manner. Roughly what
 * happens here is:
 *
 * ApplicationThread:
 *     sendMessage()
 *    wait();
 *
 * StackThread:
 *     processMessage/Timeout()
 *     {
 *          saveMessage();
 *          notify();
 *     }
 *
 *
 * <p>Organisation: <p> Louis Pasteur University, Strasbourg, France</p>
 * <p>Network Research Team (http://www-r2.u-strasbg.fr)</p></p>
 * @author Emil Ivov
 * @version 0.1
 */
class BlockingRequestSender
    implements ResponseCollector
{
    private StunProvider             stunProvider  = null;
    private NetAccessPointDescriptor apDescriptor  = null;

    StunMessageEvent responseEvent = null;

    BlockingRequestSender(StunProvider             stunProvider,
                          NetAccessPointDescriptor apDescriptor)
    {
        this.stunProvider = stunProvider;
        this.apDescriptor = apDescriptor;
    }

    /**
     * Saves the message event and notifies the discoverer thread so that
     * it may resume.
     * @param evt the newly arrived message event.
     */
    public synchronized void processResponse(StunMessageEvent evt)
    {
        this.responseEvent = evt;
        notifyAll();
    }

    /**
     * Notifies the discoverer thread when a message has timeouted so that
     * it may resume and consider it as unanswered.
     */
    public synchronized void processTimeout()
    {
        notifyAll();
    }

    /**
     * Sends the specified request and blocks until a response has been
     * received or the request transaction has timed out.
     * @param request the reuqest to send
     * @param serverAddress the request destination address
     * @return the event encapsulating the response or null if no response
     * has been received.
     * @throws StunException NETWORK_ERROR or other if we fail to send
     * the message
     */
    public synchronized StunMessageEvent sendRequestAndWaitForResponse(
                                                    Request request,
                                                    StunAddress serverAddress)
            throws StunException
    {
        stunProvider.sendRequest(request, serverAddress, apDescriptor,
                                 BlockingRequestSender.this);

        try
        {
            wait();
        }
        catch (InterruptedException ex)
        { /** @todo log */
            ex.printStackTrace();
        }

        StunMessageEvent res = responseEvent;
        responseEvent = null; //prepare for next message

        return res;
    }
}
