package com.interpress_project.modernshare.client.controller.delegate;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.util.Properties;

import org.jacorb.security.sas.GssUpContext;
import org.omg.CORBA.COMM_FAILURE;
import org.omg.CORBA.NO_PERMISSION;
import org.omg.CORBA.TRANSIENT;
import org.omg.CORBA_2_3.ORB;

import com.interpress_project.modernshare.AppKeys;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.AccountExistException;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.AccountNotFoundException;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.ConnectionFailException;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.InternalFailException;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.LicenseException;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.NoPermissionException;
import com.interpress_project.modernshare.client.controller.delegate.exceptions.UnknownHostException;
import com.interpress_project.modernshare.client.controller.model.LocalModelManager;
import com.interpress_project.modernshare.client.controller.model.PropertyManager;
import com.interpress_project.modernshare.ipcommon.SystemBase;
import com.interpress_project.modernshare.orb.MdsClientInfoFactory;
import com.interpress_project.modernshare.orb.MdsClientInfoImpl;
import com.interpress_project.modernshare.orb.MdsServerInfoFactory;
import com.interpress_project.modernshare.orb.generated.AccountExist;
import com.interpress_project.modernshare.orb.generated.AccountNotFound;
import com.interpress_project.modernshare.orb.generated.InternalError;
import com.interpress_project.modernshare.orb.generated.LicenseError;
import com.interpress_project.modernshare.orb.generated.MdsClientInfo;
import com.interpress_project.modernshare.orb.generated.MdsClientInfoHelper;
import com.interpress_project.modernshare.orb.generated.MdsServerInfo;
import com.interpress_project.modernshare.orb.generated.MdsServerInfoHelper;
import com.interpress_project.modernshare.orb.generated.MdsService;
import com.interpress_project.modernshare.orb.generated.MdsServiceHelper;
import com.interpress_project.modernshare.orb.generated.NoPrivilege;

/**
 * Represents a singleton.
 * @stereotype Singleton factory
 */
public class ORBManager {
	private final SystemBase sb = SystemBase.getInstance();
	private final PropertyManager propmgr = PropertyManager.getInstance();
	private Properties props = null;
	private ORB orb = null;
	private MdsService service = null;
	private String corbaurl; //, account;

	/**
	 * Holds singleton instance
	 */
	private static ORBManager instance;

	/**
	 * ORBManager
	 */
	protected ORBManager() {
		props = System.getProperties();
		props.setProperty("org.omg.CORBA.ORBClass", "org.jacorb.orb.ORB");
		props.setProperty("org.omg.CORBA.ORBSingletonClass", "org.jacorb.orb.ORBSingleton");
		props.setProperty("custom.props", AppKeys.ORBPropFilename);
		props.setProperty("jacorb.suppress_no_props_warning", "on");
		props.setProperty("jacorb.security.sas.contextClass", "org.jacorb.security.sas.GssUpContext");
		props.setProperty("org.omg.PortableInterceptor.ORBInitializerClass.SAS", "org.jacorb.security.sas.SASInitializer");
		props.setProperty("org.omg.PortableInterceptor.ORBInitializerClass.GSSUPProvider",
		  "org.jacorb.security.sas.GSSUPProviderInitializer");
		props.setProperty("jacorb.security.keystore", propmgr.getKeystore());
		props.setProperty("jacorb.security.keystore_password", propmgr.getKeystorePass());
	}

	/**
	 * Returns the singleton instance.
	 @return	the singleton instance
	 */
	static public ORBManager getInstance() {
		if (instance == null) {
			instance = new ORBManager();
		}
		return instance;
	}

	/**
	 * updateSVNServerInfo
	 * @throws NoPermissionException
	 * @throws ConnectionFailException
	 * @throws UnknownHostException
	 */
	public void updateSVNServerInfo() throws NoPermissionException, ConnectionFailException, UnknownHostException {
		try {
			_connect(propmgr.getUsername(), propmgr.getPassword());
			_updateSVNServerInfo();
		}
		finally {
			disconnect();
		}
	}

	/**
	 * updateClientData
	 * @throws UnknownHostException 
	 * @throws ConnectionFailException 
	 * @throws NoPermissionException 
	 * @throws ConnectionFailException 
	 */
	public void updateClientData() throws NoPermissionException, UnknownHostException, ConnectionFailException {
		LocalModelManager lmmgr = LocalModelManager.getInstance();
		try {
			if (lmmgr.hasXMLLoaded() == false) { // XML file not yet loaded.
				sb.getLogger().debug("Need load Config XML file from the remote.");

				_connect(propmgr.getUsername(), propmgr.getPassword());
				_updateClientData(propmgr.getUsername(), propmgr.getClientId());
				lmmgr.needXMLSave(false);
			}
		}
		finally {
			disconnect();
		}
	}

	/**
	 * storeClientData
	 * @throws NoPermissionException
	 * @throws ConnectionFailException
	 * @throws UnknownHostException
	 */
	public void storeClientData() throws NoPermissionException, ConnectionFailException, UnknownHostException {
		LocalModelManager lmmgr = LocalModelManager.getInstance();
		try {
			if (lmmgr.needXMLSave()) {
				sb.getLogger().debug("Need remote store of Config XML file.");

				_connect(propmgr.getUsername(), propmgr.getPassword());
				_storeClientData(propmgr.getUsername(), propmgr.getClientId());
				lmmgr.needXMLSave(false);
			}
		}
		finally {
			disconnect();
		}
	}

	/**
	 * createAccount
	 * @param rootaccount
	 * @param rootpassword
	 * @param account
	 * @param password
	 * @throws NoPermissionException
	 * @throws ConnectionFailException
	 * @throws UnknownHostException
	 * @throws LicenseException 
	 * @throws AccountExistException 
	 * @throws InternalFailException 
	 */
	public void createAccount(String rootaccount, String rootpassword, String account, String password)
	    throws NoPermissionException, ConnectionFailException, UnknownHostException, InternalFailException,
	    AccountExistException, LicenseException {
		try {
			_connect(rootaccount, rootpassword);
			_updateSVNServerInfo();
			_createAccount(account, password);
		}
		finally {
			disconnect();
		}
	}

	/**
	 * deleteAccount
	 * @param rootaccount
	 * @param rootpassword
	 * @param account
	 * @throws UnknownHostException 
	 * @throws ConnectionFailException 
	 * @throws NoPermissionException 
	 * @throws AccountNotFoundException 
	 * @throws InternalFailException 
	 */
	public void deleteAccount(String rootaccount, String rootpassword, String account) throws NoPermissionException,
	    ConnectionFailException, UnknownHostException, InternalFailException, AccountNotFoundException {
		try {
			_connect(rootaccount, rootpassword);
			_updateSVNServerInfo();
			_deleteAccount(account);
		}
		finally {
			disconnect();
		}
	}

	/**
	 * changePassword
	 * @param rootaccount
	 * @param rootpassword
	 * @param account
	 * @param newpassword
	 * @throws UnknownHostException 
	 * @throws ConnectionFailException 
	 * @throws NoPermissionException 
	 * @throws AccountNotFoundException 
	 */
	public void changePassword(String rootaccount, String rootpassword, String account, String newpassword)
	    throws NoPermissionException, ConnectionFailException, UnknownHostException, AccountNotFoundException {
		try {
			_connect(rootaccount, rootpassword);
			_updateSVNServerInfo();
			_changePassword(account, newpassword);
		}
		finally {
			disconnect();
		}
	}

	/**
	 * backup
	 * @param rootaccount
	 * @param rootpassword
	 * @return
	 * @throws UnknownHostException 
	 * @throws ConnectionFailException 
	 * @throws NoPermissionException 
	 * @throws InternalFailException 
	 */
	public String backup(String rootaccount, String rootpassword) throws NoPermissionException, ConnectionFailException,
	    UnknownHostException, InternalFailException {
		String path = null;
		try {
			_connect(rootaccount, rootpassword);
			_updateSVNServerInfo();
			path = _backup();
		}
		finally {
			disconnect();
		}
		return path;
	}

	/**
	 * recovery
	 * @param rootaccount
	 * @param rootpassword
	 * @throws UnknownHostException 
	 * @throws ConnectionFailException 
	 * @throws NoPermissionException 
	 * @throws InternalFailException 
	 */
	public void recovery(String rootaccount, String rootpassword) throws NoPermissionException, ConnectionFailException,
	    UnknownHostException, InternalFailException {
		try {
			_connect(rootaccount, rootpassword);
			_updateSVNServerInfo();
			_recovery();
		}
		finally {
			disconnect();
		}
	}

	/**
	 * _connect
	 * @param account
	 * @param password
	 * @throws NoPermissionException
	 * @throws ConnectionFailException
	 * @throws UnknownHostException
	 */
	private void _connect(String account, String password) throws NoPermissionException, ConnectionFailException,
	    UnknownHostException {
		sb.getLogger().debug("Enter ORBManager.connect()");

		/**
		 * Initialize ORB
		 */
		try {
			orb = (org.omg.CORBA_2_3.ORB) ORB.init(new String[] { "" }, props);
			orb.register_value_factory(MdsServerInfoHelper.id(), new MdsServerInfoFactory());
			orb.register_value_factory(MdsClientInfoHelper.id(), new MdsClientInfoFactory());
		}
		catch (Exception ex) {
			throw new ConnectionFailException("ORBの初期化に失敗しました。: " + ex.getMessage());
		}

		/**
		 * Setup auth information.
		 */
		GssUpContext.setUsernamePassword(account, password);

		// Check if the hostname is valid.
		try {
			InetAddress.getByName(propmgr.getAdminHost());
		}
		catch (java.net.UnknownHostException ex) {
			throw new UnknownHostException("データセンタ名" + propmgr.getAdminHost() + "のアドレスが不明です。データセンタ名が正しいか確認をしてください。", ex);
		}

		/**
		 * Get a connection to the server.
		 */
		corbaurl = propmgr.getTargetCorbaloc();
		org.omg.CORBA.Object obj = orb.string_to_object(corbaurl);
		try {
			service = MdsServiceHelper.narrow(obj);
		}
		catch (TRANSIENT ex) {
			throw new ConnectionFailException("データセンタ" + propmgr.getAdminHost()
			    + "へ接続中にORBでタイムアウトが発生しました。データセンタのホスト情報を確認してください。", ex);
		}
		catch (NO_PERMISSION ex) {
			throw new NoPermissionException("データセンタへのORBアクセス権限がありません。アカウント/パスワードを確認してください。", ex);
		}
		catch (COMM_FAILURE ex) {
			throw new ConnectionFailException("データセンタ" + propmgr.getAdminHost() + "との通信に失敗しました。(" + ex.getMessage() + ")", ex);
		}
	}

	/**
	 * _updateSVNServerInfo
	 */
	private void _updateSVNServerInfo() {
		sb.getLogger().debug("Enter updateSVNInfo()");

		/**
		 *  Retrieve port number for SVN server.
		 */
		MdsServerInfo info = service.getServerInfo();

//		propmgr.setSVNHost(info.getSVNHost());
//		propmgr.setSVNPort(info.getSVNPort());

//		String licType = info.getLicType();
//		propmgr.setLicType(licType);

//		sb.getLogger().debug("SVN Port number updated: " + info.getSVNPort());
		sb.getLogger().debug("Leaving updateSVNInfo()");
	}

	/**
	 * _updateClientData
	 * @param account
	 */
	private void _updateClientData(String account, String clientid) {
		sb.getLogger().debug("Enter getClientData()");

		MdsClientInfo info = service.getClientInfo(account, clientid);
		String xmldata = info.getXMLdata();
		try {
			FileOutputStream fos = new FileOutputStream(propmgr.getXMLFileName());
			OutputStreamWriter osw = new OutputStreamWriter(fos, AppKeys.ENCODING);
			BufferedWriter bw = new BufferedWriter(osw);
			bw.write(xmldata);
			bw.close();
		}
		catch (Exception ex) {
			sb.getLogger().error("Exception writing XML file : ", ex);
			return;
		}

		LocalModelManager.getInstance().load();

		sb.getLogger().debug("Leave getClientData()");
	}

	/**
	 * _storeClientData
	 * @param account
	 * @param clientid
	 */
	private void _storeClientData(String account, String clientid) {
		sb.getLogger().debug("Enter setClientData()");

		StringBuffer strbuff = new StringBuffer();

		try {
			FileInputStream fis = new FileInputStream(propmgr.getXMLFileName());
			InputStreamReader isr = new InputStreamReader(fis, AppKeys.ENCODING);
			BufferedReader br = new BufferedReader(isr);

			String str = null;
			while ((str = br.readLine()) != null) {
				strbuff.append(str);
			}
		}
		catch (Exception ex) {
			sb.getLogger().error("Exception reading XML file: ", ex);
			return;
		}
		service.setClientInfo(new MdsClientInfoImpl(account, clientid, strbuff.toString()));

		sb.getLogger().debug("Leave setClientData()");
	}

	/**
	 * _createAccount
	 * @param account
	 * @param password
	 * @throws InternalFailException
	 * @throws NoPermissionException
	 * @throws AccountExistException
	 * @throws LicenseException
	 */
	private void _createAccount(String account, String password) throws InternalFailException, NoPermissionException,
	    AccountExistException, LicenseException {
		sb.getLogger().debug("Enter ORBManager.createAccount(" + account + ")");

		try {
			service.addUser(account, password);
		}
		catch (InternalError ex) {
			throw new InternalFailException(ex);
		}
		catch (AccountExist ex) {
			throw new AccountExistException("アカウント" + account + "は、既にデータセンタに登録されています。");
		}
		catch (NoPrivilege ex) {
			throw new NoPermissionException("管理者アカウントの権限がありません。");
		}
		catch (LicenseError ex) {
			throw new LicenseException("現行のライセンスで管理できるアカウントは1つまでです。 ");
		}
		sb.getLogger().debug("ORBManager.createAccount(" + account + ") successful.");
	}

	/**
	 * _deleteAccount
	 * @param account
	 * @throws InternalFailException 
	 * @throws AccountNotFoundException 
	 * @throws NoPermissionException 
	 */
	private void _deleteAccount(String account) throws InternalFailException, AccountNotFoundException,
	    NoPermissionException {
		sb.getLogger().debug("Enter ORBManager.deleteAccount(" + account + ")");

		try {
			service.deleteUser(account);
		}
		catch (InternalError ex) {
			throw new InternalFailException(ex);
		}
		catch (AccountNotFound ex) {
			throw new AccountNotFoundException("データセンタに、アカウント" + account + "は登録されていません。", ex);
		}
		catch (NoPrivilege ex) {
			throw new NoPermissionException("管理者アカウントの権限がありません。");
		}
		sb.getLogger().debug("Enter ORBManager.deleteAccount(" + account + ") successful.");
	}

	/**
	 * _changePassword
	 * @param account
	 * @param newpassword
	 * @throws AccountNotFoundException
	 */
	private void _changePassword(String account, String newpassword) throws AccountNotFoundException {
		sb.getLogger().debug("Enter ORBManager.changePassword(" + account + ")");

		try {
			service.changePassword(account, newpassword);
		}
		catch (AccountNotFound ex) {
			throw new AccountNotFoundException("データセンタに、アカウント" + account + "は登録されていません。", ex);
		}
		sb.getLogger().debug("Enter ORBManager.changePassword(" + account + ") successful.");
	}

	/**
	 * _backup
	 * @return
	 * @throws InternalFailException
	 * @throws NoPermissionException
	 */
	private String _backup() throws InternalFailException, NoPermissionException {
		sb.getLogger().debug("Enter ORBManager.backup()");

		String path = null;
		try {
			path = service.backup();
		}
		catch (InternalError ex) {
			throw new InternalFailException(ex);
		}
		catch (NoPrivilege e) {
			throw new NoPermissionException("管理者アカウントの権限がありません。");
		}
		sb.getLogger().debug("Enter ORBManager.backup() successful.");
		return path;
	}

	/**
	 * _recovery
	 * @throws InternalFailException
	 * @throws NoPermissionException
	 */
	private void _recovery() throws InternalFailException, NoPermissionException {
		sb.getLogger().debug("Enter ORBManager.recovery()");
		try {
			service.recovery();
		}
		catch (InternalError ex) {
			throw new InternalFailException(ex);
		}
		catch (NoPrivilege ex) {
			throw new NoPermissionException("管理者アカウントの権限がありません。");
		}
		sb.getLogger().debug("Enter ORBManager.recovery() successful.");
	}

	/**
	 * disconnect
	 */
	private void disconnect() {
		if (orb != null) {
			orb.shutdown(false);
			orb = null;
		}
	}
}
