package me.chyxion.tigon.shiro;

import java.util.List;
import org.slf4j.Logger;
import java.io.Serializable;
import java.util.LinkedList;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.annotation.PostConstruct;
import org.springframework.util.Assert;
import org.apache.shiro.session.Session;
import org.apache.shiro.web.util.WebUtils;
import org.apache.shiro.web.servlet.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

/**
 * @version 0.0.1
 * @since 0.0.1
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Apr 7, 2015 2:54:25 PM
 */
public class DefaultWebSessionManagerExt extends DefaultWebSessionManager {
    private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManagerExt.class);
    @Autowired(required = false)
    private SessionIdManager sessionIdManager;
    @Autowired(required = false)
    private List<SessionListener> sessionListeners;

    /**
     * init
     */
    @PostConstruct
    void init() {
        if (sessionListeners == null) {
            sessionListeners = new LinkedList<SessionListener>();
        }
        else {
            log.info("Session listeners [{}] found.", sessionListeners);
        }
        setSessionListeners(sessionListeners);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onStart(Session session, SessionContext context) {
        if (!WebUtils.isHttp(context)) {
            log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response " +
                    "pair. No session ID cookie will be set.");
            return;
        }
        HttpServletRequest request = WebUtils.getHttpRequest(context);
        HttpServletResponse response = WebUtils.getHttpResponse(context);
        Serializable sessionId = session.getId();
        if (isSessionIdCookieEnabled()) {
            writeSessionIdCookie(sessionId, request, response);
        } 
        else {
            log.debug("Session id cookie is disabled.  no cookie has been set for new session with id [{}]", sessionId);
        }
        request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
        request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        Serializable sessionId = null;
        if (sessionIdManager != null) {
            log.debug("Get custom session id from custom session manager.");
            sessionId = sessionIdManager.read(
                (HttpServletRequest) request, 
                (HttpServletResponse) response,
                new SimpleCookie(getSessionIdCookie()));
        }
        if (sessionId == null) {
            log.debug("No custom session id found from custom session manager, get session id from cookie.");
            sessionId = super.getSessionId(request, response);
        }
        return sessionId;
    }

    // --
    // private methods

    void writeSessionIdCookie(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(currentId, "Session id cannot be null when persisting for subsequent requests");
        String sessionId = currentId.toString();
        // copy cookie template
        Cookie cookie = new SimpleCookie(getSessionIdCookie());
        cookie.setValue(sessionId);
        if (sessionIdManager != null) {
            if (sessionIdManager.write(request, response, cookie)) {
                log.info("Custom session id manager write session id cookie [{}] for session [{}].", cookie, sessionId);
                return;
            }
            else {
                log.info("Custom session id manager dose not write session id cookie [{}] for session [{}].", cookie, sessionId);
            }
        }
        log.info("Default write session id cookie [{}] for session [{}].", cookie, sessionId);
        cookie.saveTo(request, response);
    }
}
