package sample.application;

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

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;

/**
 * <pre>
 * Sample3AnsweredCall code sample : it is based on the Sample1MakeCall's code
 * 
 *   Scenario :
 *   Gets users sample.config and authenticates them.
 *   The future callee subscribe to telephonic events.
 *   User 1 calls user 2 (the make call is initiated)
 *   The callee is ringing and answers after a short time (it has used call reference to do this).
 *   After 7s of conversation the callee releases the incoming call using the callRef.
 *   Subscription is closed.
 *   At last the session is closed.
 *
 *   Note:
 *   To be able to make this scenario work, both devices have handle correctly the 3PCC.
 *   Furthermore the caller's device has to be in auto-answer mode (can be replaced by answering
 *   manually when the caller is ringing), and the callee device, when it is a My IC Phone 8082,
 *   must be at least a R300 version (set with 8770).
 * </pre>
 */
public class Sample3AnsweredCall {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(Sample3AnsweredCall.class);

    public static void main(String[] args) throws OTRestClientException {
        
        ClientSession user1Session = null;
        ClientSession user2Session = null;
        ClientSubscription user2Subscription = null;

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

            // User 1 and 2 authentications
            Authentication user1Authentication = new Authentication();
            user1Authentication.authenticate(user1Config);
            Authentication user2Authentication = new Authentication();
            user2Authentication.authenticate(user2Config);

            // Get a session manager per user
            user1Session = new ClientSession(user1Config.getLogin(), user1Authentication);
            user2Session = new ClientSession(user2Config.getLogin(), user2Authentication);

            // Users open session
            // a session is needed for the caller (user1) to be able to initiate a make-call
            // and for the callee (user2) to be able to answer the incoming call.
            user1Session.open();
            user2Session.open();

            // Create a call control for both users
            CallControl user1CallControl = new CallControl(user1Config, user1Authentication.getCookie(), user1Session);
            CallControl user2CallControl = new CallControl(user2Config, user2Authentication.getCookie(), user2Session);

            // User 2 subscribes to telephonic events
            user2Subscription = new ClientSubscription(user2Config.getLogin(), user2Authentication.getCookie(), user2Session);
            user2Subscription.subscribe(EventPackages.TELEPHONY.getPackage());
            Future<String> callRefFuture = user2Subscription.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());
            user1CallControl.makeCallRequest(user2Config);

            // User 2 "waits" for the first event with a call-ref:
            String callRef = getCallRef(callRefFuture);

            // User 2 rings, wait 3s before answering
            waitInSeconds(3);

            //
            // ANSWER CALL : User 2 answers
            //
            LOGGER.debug("Callee {} answers the call", user2Config.getLogin());
            user2CallControl.answerCallRequest(callRef);

            // Conversation between both users
            waitInSeconds(8);

            //
            // RELEASE CALL : User 2 (callee) releases the call and after unsubscribe from telephony events
            //
            LOGGER.debug("{} releases the call", user2Config.getLogin());
            user2CallControl.releaseCallRequest(callRef);

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

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

    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);
        }
    }
}
