/*
 * Open Source Business Intelligence Tools - http://www.osbitools.com/
 * 
 * Copyright 2014-2016 IvaLab Inc. and by respective contributors (see below).
 * 
 * Released under the LGPL v3 or higher
 * See http://www.gnu.org/licenses/lgpl.txt
 *
 * Date: 2014-10-02
 * 
 * Contributors:
 * 
 * Igor Peonte <igor.144@gmail.com>
 *
 */

package com.osbitools.ws.shared.auth;

import java.security.Principal;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.*;
import com.osbitools.ws.shared.WsSrvException;

/**
 * 
 * Security provider based on Tomcat Users file structure
 * 
 * @author "Igor Peonte <igor.144@gmail.com>"
 * 
 */
public abstract class SessionSecurityProvider
                          extends AbstractSessionSecurityProvider {

	static ConcurrentHashMap<String, Session> activeSessions;
				
	@Override
	public void init(ServletContext ctx, 
                    Properties properties) throws RuntimeException {
	  super.init(ctx, properties);
	  
   synchronized(this) {
      if (activeSessions == null)
        activeSessions = new ConcurrentHashMap<String,Session>();
    }
	}
	
  protected Session createUserSession(HttpServletRequest req, 
                          String user, String stoken, Object params) {
    return createUserSession(req, new LoginUser(user),stoken, params);
  }

  protected Session createUserSession(HttpServletRequest req, 
                          LoginUser user, String stoken, Object params) {
    return createUserSession(req, stoken, new Session(
                                user, stoken, getTimeout(), params));
  }
  
  @Override
  protected synchronized Session createUserSession(HttpServletRequest req, 
                                            String stoken, Session session) {
    // Check if session exists otherwise refresh
    Session s = activeSessions.get(stoken);
    if (s == null)
      activeSessions.put(stoken, session);
    else
      s.refresh(getTimeout());
    
    return session;
  }
  
	@Override
	public void validate(HttpServletRequest req, String token) 
                                                  throws WsSrvException {
		findSession(req, token);
	}

	protected Session findSession(HttpServletRequest req, 
                                String token) throws WsSrvException {
		Session session = activeSessions.get(token);
	    
		if (session == null)
		  //-- 3
		  throw new WsSrvException(3, "",
						"Invalid token '" + token + "'");
		else if (session.isFinished())
		  //-- 4
			throw new WsSrvException(4, "",
						"Session '" + token + "' completed");
			
		return session;
	}
	
  public synchronized void logout(HttpServletRequest req, 
                            String stoken) throws WsSrvException {
		Session session = findSession(req, stoken);
		
		session.logout();
	}

	@Override
  public String getUser(HttpServletRequest req, 
                        String stoken) throws WsSrvException {
	  return findSession(req, stoken).getUser().getName();
  }
}

class Session {
	// Date Stamp when was started
	private final long _started;
	
	// Date Stamp when session will be finished
	private long _finished;
	
	// User who open this session
	private final LoginUser _user;
	
	// Security Token
	private final String _token;

	// Date Stamp when it was terminated by logout or by validation
	private long _terminated = 0;
	
	// Custom parameters
	private Object _params;
	
	public Session(LoginUser user, String stoken, long timeout) {
    this(user, stoken, timeout, null);
  }
	
	public Session(LoginUser user, String stoken, long timeout, Object params) {
		_started = System.currentTimeMillis();
		_user = user;
		_token = stoken;
		_params = params;
		_finished = _started + timeout;
	}
	
	public void refresh(long timeout) {
	  _terminated = 0;
	  _finished = System.currentTimeMillis() + timeout;
	}
	
	public void logout() {
	  _terminated = System.currentTimeMillis();
	}
	
	public boolean isFinished() {
	  boolean fdone = _terminated != 0;
	  if (!fdone && System.currentTimeMillis() > _finished)
	    logout();
	  
		return _terminated != 0;
	}

	public long getStarted() {
		return _started;
	}

	public LoginUser getUser() {
		return _user;
	}

	public String getToken() {
		return _token;
	}
	
	/**
	 * Return time in msec when session was terminated 
	 *                           either by logout or by validation
	 * @return
	 */
	public long getTerminated() {
	  return _terminated;
	}
	
	public Object getCustomParams() {
	  return _params;
	}
	
	public void setCustomParams(Object params) {
	  _params = params;
	}
}

class LoginUser implements Principal {

  // UserName
  private final String _usr;
  
  // Login Time
  private final long _dts;

  public LoginUser(User user) {
    this(user.getUsername());
  }
  
  public LoginUser(String username) {
    _usr = username;
    _dts = System.currentTimeMillis();
  }
  
  @Override
  public String getName() {
    return _usr;
  }
  
  public long getLoginTime() {
    return _dts;
  } 
}
