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 Sample2Subscription and communication log event subscription
 *
 *   First of all it gets users sample.config, and then authenticates them.
 *   After, for both users, it opens a session and subscribes to events : telephonic for the caller and communication log events for the callee.
 *   And then only the make call is initiated; user 1 calls user 2, the callee is ringing but doesn't answer.
 *   Subscription for the caller is closed, as at this time, it has received the call reference it needs to release the call.
 *   After 7s the call is released by the caller, the callee doesn't ring anymore.
 *   The callee will receive a "missed call" event through its subscription. This reception also closes the subscription.
 *   At last both sessions are 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 Sample6CommunicationLog {

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

    private static final String ABANDONED = "ABANDONED";

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

        ClientSession user1Session = null;
        ClientSession user2Session = null;
        ClientSubscription user1Subscription = 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");

            // Caller and callee authentication
            Authentication user1Authentication = new Authentication();
            user1Authentication.authenticate(user1Config);
            Authentication user2Authentication = new Authentication();
            user2Authentication.authenticate(user2Config);

            // Caller and callee session manager
            user1Session = new ClientSession(user1Config.getLogin(), user1Authentication);
            user2Session = new ClientSession(user2Config.getLogin(), user2Authentication);

            // Caller opens session
            user1Session.open(); // a session is needed for the caller to be able to initiate a make-call
            user2Session.open(); // a session is needed for the callee to be able to subscribe to events

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

            // Callee subscribes to communication log events
            user2Subscription = new ClientSubscription(user2Config.getLogin(), user2Authentication.getCookie(), user2Session);
            user2Subscription.subscribe(EventPackages.UNIFIEDCOMLOG.getPackage());
            Future<String> onComRecordCreatedAnsweredStatusFuture = user2Subscription.waitForMissedCallEvent();

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

            //
            // MAKE CALL : Caller (User 1) calls callee (User 2)
            // Note that the caller's device has to handle the 3PCC.
            // Its 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);

            // Caller has now the call ref, subscription is no more needed
            user1Subscription.unsubscribe();
            user1Subscription = null;

            // Callee is ringing, wait...
            LOGGER.debug("Wait...");
            waitInSeconds(7);

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

            // "Wait" for the first event with a call-ref
            boolean onComRecordCreatedAnsweredStatus = getOnComRecordAnsweredStatus(onComRecordCreatedAnsweredStatusFuture);
            if (!onComRecordCreatedAnsweredStatus) {
                LOGGER.info("*** Awaited communication log '{}' received -> Missed Call ***\n", ABANDONED);
            } else {
                LOGGER.info("Communication log received : {}", onComRecordCreatedAnsweredStatus);
            }

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

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

            // Close users sessions
            try {
                if (user1Session != null) {
                    user1Session.close();
                }
            } catch (OTRestClientException e) {
                LOGGER.info("Couldn't close caller session : ", e);
            }
            try {
                if (user2Session != null) {
                    user2Session.close();
                }
            } catch (OTRestClientException e) {
                LOGGER.info("Couldn't close callee 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 future object which the get method will be called
     * @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 getting the callRef : ", e);
        }
    }

    /**
     * Wait for the first communication log onComRecordCreated event and extract its answered status
     *
     * @param onComRecordCreatedAnsweredStatusFuture future object which the get method will be called
     * @return The com log OnComRecordCreated answered status
     * @throws sample.core.exception.OTRestClientException
     */
    private static boolean getOnComRecordAnsweredStatus(Future<String> onComRecordCreatedAnsweredStatusFuture) throws OTRestClientException {
        try {
            return Boolean.valueOf(onComRecordCreatedAnsweredStatusFuture.get()); // The get is blocking
        } catch (InterruptedException | ExecutionException e) {
            throw new OTRestClientException("Failure in getting right communication log event : ", e);
        }
    }
}
