package sample.application;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.config.UserConfig;
import sample.core.Authentication;
import sample.core.ClientSession;
import sample.core.ClientSubscription;
import sample.core.EventPackages;
import sample.core.exception.OTRestClientException;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <pre>
 * Code sample based on Sample4SessionKeepAlive which adds the handling of events subscription keep
 * alive.
 *
 *   To handle a session keep alive, we have to use timer in order to refresh the session before the
 *   server closes it.
 *
 *   For the events subscription it's a little bit different, once subscribed the timer ending will
 *   stop the subscription even if events are received. For the client there is no way to maintain
 *   the link open, so, when the socket is closed by the server, the client has to restart the chunk.
 *   It has 1 minute to reconnect to the input stream, during this time the server retains the events
 *   so that none is lost.
 *
 *   Only one user is used for this sample, it authenticates itself and opens a session which is
 *   maintained to life by a periodic activated thread.
 *   Then a subscription is done to all telephonic events. The default time to live from the
 *   subscription is 10 minutes, during these, the events are simply logged. When the server closes
 *   the subscription, the client connects again, and cycle begins again ...
 *   The scenario stops itself only after 24 hours.
 *   Note that the keep alive itself is done in ClientSubscription.keepAliveAndLogEvents();
 * </pre>
 */
public class Sample5SubscriptionKeepAlive {
    private static final Logger LOGGER = LoggerFactory.getLogger(Sample5SubscriptionKeepAlive.class);

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

        // Read the configuration of the user involved in the sample
        UserConfig userConfig = UserConfig.create("alice");

        // User authentication
        Authentication userAuthentication = new Authentication();
        userAuthentication.authenticate(userConfig);

        // Get a session manager
        final ClientSession userSession = new ClientSession(userConfig.getLogin(), userAuthentication);

        // Open session
        userSession.open();

        // Subscribe to telephonic events
        final ClientSubscription userSubscription = new ClientSubscription(userConfig.getLogin(), userAuthentication.getCookie(), userSession);

        // Add a Shutdown hook (to handle abrupt-or-not sample termination)
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                //Close user subscription
                try {
                    LOGGER.info("Closing the subscription");
                    userSubscription.unsubscribe();
                } catch (OTRestClientException e) {
                    LOGGER.info("Couldn't close user's subscription", e);
                }

                try {
                    LOGGER.info("Closing the session");
                    userSession.close();
                } catch (OTRestClientException e) {
                    LOGGER.error("Couldn't close user's session", e);
                }
            }
        });
        LOGGER.debug("Shut Down Hook Attached");

        // Get session's time to live value
        int sessionTimeToLiveInSeconds = userSession.getSessionInfo().getTimeToLive();
        int sessionRefreshTimeInSeconds = (int) (sessionTimeToLiveInSeconds * 0.8); // use of a 80% ratio
        LOGGER.debug("timeToLive : {}, sessionRefreshTimeInSeconds : {}", sessionTimeToLiveInSeconds, sessionRefreshTimeInSeconds);

        final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
        scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);

        //New thread to maintain session alive
        scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    LOGGER.info("Keep session alive");
                    userSession.keepSessionAlive();
                } catch (OTRestClientException e) {
                    LOGGER.error("Sample5SubscriptionKeepAlive keep alive aborted : ", e);
                }
            }
        },
                sessionRefreshTimeInSeconds, sessionRefreshTimeInSeconds, TimeUnit.SECONDS);

        // Subscribe to telephony events
        userSubscription.subscribe(EventPackages.TELEPHONY.getPackage());

        userSubscription.keepAliveAndLogEvents();

        waitInHours(24);

        LOGGER.debug("Wait end");
        scheduledThreadPoolExecutor.shutdownNow();
        System.exit(0); // Keep exit simple
    }

    static void waitInHours(int hours) {
        try {
            Thread.sleep(TimeUnit.MILLISECONDS.convert(hours, TimeUnit.HOURS));
        } catch (InterruptedException ignore) {
        }
    }
}

