package sample.application;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sample.config.UserConfig;
import sample.core.Authentication;
import sample.core.CallControl;
import sample.core.ClientSession;
import sample.core.ClientSubscription;
import sample.core.EventPackages;
import sample.core.exception.OTRestClientException;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * <pre>
 * Subscription code sample : it is based on the Sample1MakeCall's code
 * 
 *   First of all it gets users sample.config, and then authenticates them.
 *   And then only the caller subscribe to telephonic events.
 *   User 1 calls user 2 (the make call is initiated)
 *   The callee is ringing but doesn't answer.
 *   The caller extracts the call reference from the first telephony event it receives through
 *   subscription.
 *   After 7s the caller releases the call using the callRef, the callee doesn't ring anymore.
 *   Subscription is closed.
 *   At last the session is closed.
 *
 *   Note:
 *   To be able to make this scenario work, caller's device has to correctly support the 3PCC, and also it has to be in auto-answer mode.
 *   Auto-answer mode can be replaced by answering manually when the caller is ringing.
 *   The callee has to NOT be in auto-answer mode.
 * </pre>
 */
public class Sample2Subscription {

    private static final Logger LOGGER = LoggerFactory.getLogger(Sample2Subscription.class);

    public static void main(String[] args) throws OTRestClientException {

        ClientSession user1Session = null;
        ClientSubscription user1Subscription = null;

        try {
            // Read the configuration of the users involved in the sample
            UserConfig user1Config = UserConfig.create("alice");
            UserConfig user2Config = UserConfig.create("bob");

            // User 1 authentication
            Authentication user1Authentication = new Authentication();
            user1Authentication.authenticate(user1Config);

            // User 1 session manager
            user1Session = new ClientSession(user1Config.getLogin(), user1Authentication);

            // User 1 opens session
            user1Session.open(); // a session is needed for the user to be able to initiate a make-call

            // User 1 subscribes to telephonic events
            user1Subscription = new ClientSubscription(user1Config.getLogin(), user1Authentication.getCookie(), user1Session);
            user1Subscription.subscribe(EventPackages.TELEPHONY.getPackage());
            Future<String> callRefFuture = user1Subscription.waitForCallRefInOnCallCreated();

            // Wait before initiating the call
            waitInSeconds(1);

            //
            // MAKE CALL : User 1 calls User 2
            // Note that the caller's device has to handle the 3PCC.
            // The device also must be in auto answer mode, otherwise it will ring (in that case it can be answered manually).
            //
            LOGGER.debug("Make call {} -> {}", user1Config.getLogin(), user2Config.getLogin());
            CallControl user1CallControl = new CallControl(user1Config, user1Authentication.getCookie(), user1Session);
            user1CallControl.makeCallRequest(user2Config);

            // "wait" for the first event with a call-ref
            String callRef = getCallRef(callRefFuture);

            // User 2 is ringing, wait...
            waitInSeconds(7);

            // User 1 (the caller) releases the call
            LOGGER.debug("{} releases the call", user1Config.getLogin());
            user1CallControl.releaseCallRequest(callRef);

        } finally {
            // Close user 1 subscription
            try {
                if (user1Subscription != null) {
                    user1Subscription.unsubscribe();
                }
            } catch (OTRestClientException e) {
                LOGGER.info("Couldn't close user's 1 subscription : ", e);
            }

            // Close user 1 session
            try {
                if (user1Session != null) {
                    user1Session.close();
                }
            } catch (OTRestClientException e) {
                LOGGER.info("Couldn't close user 1 session : ", e);
            }
        }
    }

    private static void waitInSeconds(int seconds) {
        try {
            Thread.sleep(TimeUnit.MILLISECONDS.convert(seconds, TimeUnit.SECONDS));
        } catch (InterruptedException ignore) {
        }
    }

    /**
     * Wait for the first event with a call ref
     * @param callRefFuture
     * @return The Call reference
     * @throws sample.core.exception.OTRestClientException
     */
    private static String getCallRef(Future<String> callRefFuture)
        throws OTRestClientException {

        try {
            return callRefFuture.get(); // The get is blocking
        } catch (InterruptedException | ExecutionException e) {
            throw new OTRestClientException("Failure in getCallRef ", e);
        }
    }
}
