package sample.application;

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

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

import sample.config.UserConfig;
import sample.core.Authentication;
import sample.core.ClientSession;
import sample.core.exception.OTRestClientException;

/**
 * <pre>
 * Code sample showing how to open, maintain in time, and then close a Session.
 *
 *   Once opened, a session has a time limited life period. The duration is a server value which is
 *   retrieved at session opening.
 *   But the session is also maintained open by each "session access" (which restarts this timer on
 *   server's side, so client timer has also to be restarted (to not over load the server with
 *   unnecessary requests). Note that this mechanism is not handled in this sample.
 *
 *   Only one user is used for this sample, it authenticates and opens a session.
 *   The session time to live duration is get at the opening and used in a periodic activated thread.
 *   Then, each time the thread is activated, it refreshes the session. This scenario will stop
 *   itself only after 24 hours.
 *   Closing the session is done in a different way than in the 3 firsts samples : in order to handle
 *   an abrupt execution stop (CTRL-C...), a Shutdown hook is used.
 * 
 *   Note that in this example, only a keep alive from the session is shown, the Event Subscription
 *   is also subject to time outs: see Sample5SubscriptionKeepAlive.
 * </pre>
 */
public class Sample4SessionKeepAlive {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(Sample4SessionKeepAlive.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();

        // Add a Shutdown hook (to handle abrupt-or-not sample termination)
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    LOGGER.info("Closing the session");
                    userSession.close();
                } catch (OTRestClientException e) {
                    LOGGER.error("Couldn't close user session", e);
                }
            }
        });
        LOGGER.debug("Shut Down Hook Attached");

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

        // Start a new thread which handles the keep alive
        final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
        scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);

        scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    LOGGER.info("Keep session alive");
                    userSession.keepSessionAlive();
                } catch (OTRestClientException e) {
                    LOGGER.error("Sample4SessionKeepAlive keep alive aborted : ", e);
                }
            }
        }, refreshTimeInSeconds, refreshTimeInSeconds, TimeUnit.SECONDS);

        waitInHours(24);
        LOGGER.debug("Wait end");
        scheduledThreadPoolExecutor.shutdownNow();
    }

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

}
