/*
 
Copyright (C) 2009 NTT DATA INTELLILINK CORPORATION
 
This program is free software; you can redistribute it and/or
Modify it under the terms of the GNU General Public License 
as published by the Free Software Foundation, version 2.
 
This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied 
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
PURPOSE.  See the GNU General Public License for more details.
 
*/

package jp.co.intellilink.hinemos.util;

import java.lang.reflect.Method;
import java.rmi.AccessException;
import java.util.HashMap;
import java.util.Hashtable;

import javax.naming.CommunicationException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.naming.NamingContextFactory;
import org.jboss.security.auth.callback.UsernamePasswordHandler;
import org.jnp.interfaces.NamingContext;

import com.clustercontrol.accesscontrol.ejb.session.AccessCheck;
import com.clustercontrol.accesscontrol.ejb.session.AccessCheckHome;
import com.clustercontrol.util.Messages;

/**
 * ログインマネージャクラス<br>
 * 
 * @version 1.0.0
 * @since 1.0.0
 */
public class LoginManager {
	private static LoginManager m_instance = null;
	private NamingContext m_namingcontext = null;
	private LoginContext m_loginContext = null;
	private String m_url = null;
	private String m_uid = null;
	private String m_password = null;

	// 実行ログ出力用クラス
	private static Log log = LogFactory.getLog(LoginManager.class);

	/**
	 * このオブジェクトを取得します。 シングルトン
	 * 
	 * @return LoginManager ログインマネージャ
	 */
	public static LoginManager getContextManager() {
		if (m_instance == null) {
			m_instance = new LoginManager();
		}
		return m_instance;
	}

	/**
	 * コンストラクタ
	 */
	private LoginManager() {
		try {
			Configuration.setConfiguration(new HinemosClientConfig());
		} catch (Exception e) {
		}
	}

	/**
	 * ログイン(接続先URLあり)
	 * 
	 * @param uid
	 * @param password
	 * @param url
	 * @throws Exception
	 */
	public synchronized void login(String uid, String password, String url) throws Exception {
		// ログアウト
		logout();

		m_uid = uid;
		m_password = password;

		// NamingContext取得
		m_namingcontext = getContext(url);

		try {
			// ログインチェック
			AccessCheckHome home = (AccessCheckHome) m_namingcontext.lookup(AccessCheckHome.JNDI_NAME);
			AccessCheck accessCheck = home.create();
			accessCheck.checkLogin();

		} catch (Exception e) {
			logout();
			throw e;
		}
	}

	/**
	 * ログアウト
	 * 
	 * @throws NamingException
	 */
	public synchronized void logout() throws NamingException {
		if (m_loginContext instanceof LoginContext) {
			try {
				m_loginContext.logout();
				m_uid = null;

			} catch (LoginException e) {
			}
		}
		m_loginContext = null;
		m_namingcontext = null;
	}

	/**
	 * ログインチェック
	 * 
	 * @return ログイン結果
	 */
	public boolean isLogin() {
		if (m_loginContext instanceof LoginContext) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * RepositoryCollectorを取得します。
	 * 
	 * @param uid
	 * @param pw
	 * @param url
	 * @return NamingContext
	 * @throws NamingException
	 */
	public synchronized NamingContext getNamingContext(String uid, String pw, String url) throws NamingException {
		if (!isLogin()) {

			try {
				// login(dialog.getUserid(), dialog.getPassword());
				login(uid, pw, url);

			} catch (CommunicationException e) {
				// 接続失敗メッセージを出力
				log.error(Messages.getString("message.accesscontrol.22"), e);
			} catch (AccessException e) {
				// ログイン失敗メッセージを出力
				log.error(Messages.getString("message.accesscontrol.6"), e);
			} catch (Exception e) {
				// 予期せぬメッセージを出力
				log.error(Messages.getString("message.accesscontrol.23"), e);
				log.error(Messages.getString("message.accesscontrol.6"));
			}

		}
		return m_namingcontext;
	}

	/**
	 * NamingContextを取得します。
	 * 
	 * @param url
	 * @return NamingContext
	 * @throws NamingException
	 * @throws LoginException
	 */
	@SuppressWarnings("unchecked")
	private NamingContext getContext(String url) throws NamingException, LoginException {
		// 接続先URL取得
		m_url = url;

		Hashtable props = new Hashtable();
		props.put(InitialContext.PROVIDER_URL, m_url);

		UsernamePasswordHandler handler = new UsernamePasswordHandler(m_uid, m_password.toCharArray());
		m_loginContext = new LoginContext("hinemosClient", handler);
		m_loginContext.login();

		NamingContextFactory ncf = new NamingContextFactory();
		NamingContext namingContext = (NamingContext) ncf.getInitialContext(props);

		return namingContext;
	}

	/**
	 * HinemosClient用のコンフィギュレーションクラス
	 * 
	 * @version 3.0.0
	 * @since 3.0.0
	 */
	class HinemosClientConfig extends Configuration {
		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.security.auth.login.Configuration#refresh()
		 */
		public void refresh() {

		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.security.auth.login.Configuration#getAppConfigurationEntry(java.lang.String)
		 */
		@SuppressWarnings("unchecked")
		public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
			AppConfigurationEntry[] entry = null;
			try {
				Class[] parameterTypes = {};
				Method m = getClass().getDeclaredMethod(name, parameterTypes);
				Object[] args = {};
				entry = (AppConfigurationEntry[]) m.invoke(this, args);
			} catch (Exception e) {
			}
			return entry;
		}

		/**
		 * Hinemos Client Configuration
		 * 
		 * @return
		 */
		@SuppressWarnings("unchecked")
		AppConfigurationEntry[] hinemosClient() {
			String name = "org.jboss.security.ClientLoginModule";
			HashMap options = new HashMap();
			options.put("multi-threaded", "true");
			options.put("restore-login-identity", "true");
			AppConfigurationEntry ace = new AppConfigurationEntry(name,
					AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
			AppConfigurationEntry[] entry = { ace };
			return entry;
		}
	}
	
	/**
	 * ログインユーザIDを取得する。
	 * 
	 * @return ログインユーザID
	 */
	public String getUserId(){
		return m_uid;
	}
}