package jp.co.denso.fa.jmscaosql;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import jp.co.denso.fa.jcaosql.CaoSQLController;
import jp.co.denso.fa.jcaosql.CaoSQLControllerListener;
import jp.co.denso.fa.jcaosql.CaoSQLControllerListenerEx;
import jp.co.denso.fa.jcaosql.CaoSQLEngine;
import jp.co.denso.fa.jcaosql.CaoSQLItem;
import jp.co.denso.fa.jcaosql.CaoSQLMessage;
import jp.co.denso.fa.jcaosql.NullComInterfaceException;
import jp.co.denso.fa.jcaosql.UnsupportedTypeException;

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

/**
 * CaoSQLのイベントを監視し、受信した通知内容をJMSのメッセージとして送信する機能を提供するクラスです。<br>
 *
 * @author DENSO WAVE
 * @version 1.00
 */
public class JmsCaoSQLClient extends AbstructJmsClient {

	/**
	 * ロガー
	 */
	private static Logger logger = LoggerFactory.getLogger(JmsCaoSQLClient.class);

	/**
	 * CaoSQLEngine
	 */
	private CaoSQLEngine engine = null;

	/**
	 * イベント監視対象のCaoSQLControllerを保持します。
	 */
	private Map<String, CaoSQLController> ctrlMap = null;

	/**
	 * @see AbstructJmsClien.classt#init()
	 */
	@Override
	public List<String> init() throws Exception {
		// プロパティから対象コントローラー名を取得
		List<String> targetCtrlList = config.getStringList(KEY_CONTROLLER_NAMES);

		// CaoSQLEngineのインスタンス生成
		engine = CaoSQLEngine.createInstance();
		sleep(1000);
		if (engine.count() < 1) {
			throw new Exception("There is no controller settings.");
		}

		// CaoSQLEngineからCaoSQLController一覧を取得。
		List<String> ctrlNameList = Arrays.asList(engine.getControllerNames());

		// プロパティにコントローラー名が設定されていない場合
		if (targetCtrlList.isEmpty()) {
			// 全てのコントローラーをイベント監視対象にする
			targetCtrlList = ctrlNameList;
		}

		ctrlMap = new HashMap<String, CaoSQLController>();
		List<String> errorControllers = new ArrayList<String>();
		// 指定されたコントローラーに対してイベント監視を開始する
		for (final String configCtrlName : targetCtrlList) {
			if (!ctrlNameList.contains(configCtrlName)) {
				logger.warn("{} does not exist in CaoSQLEngine.", configCtrlName);
				errorControllers.add(configCtrlName);
				continue;
			}

			try {
				attachListener(configCtrlName);
			} catch (Exception e) {
				logger.error(String.format("Unable to attatch listener of {}.", configCtrlName), e);
				errorControllers.add(configCtrlName);
			}
		}

		// プロパティに指定されているControllerが1つも存在しない場合
		if (ctrlMap.isEmpty()) {
			throw new Exception("There is no valid candidate.");
		}

		isRunning = true;

		return errorControllers;
	}

	/**
	 * @see AbstructJmsClient.class#stop()
	 */
	@Override
	public void stop() {
		// イベント監視終了
		// CaoSQLController解放
		if (ctrlMap != null) {
			for (Entry<String, CaoSQLController> entry : ctrlMap.entrySet()) {
				CaoSQLController ctrl = entry.getValue();
				if (ctrl != null) {
					logger.debug("{} is released.", entry.getKey());
					ctrl.detachListener();
					ctrl.release();
					ctrl = null;
				}
			}
			ctrlMap.clear();
		}

		// CaoSQLEngine解放
		if (engine != null) {
			logger.debug("CaoSQLEngine is released.");
			engine.release();
			engine = null;
		}

		System.gc();
		isRunning = false;
	}

	/**
	 * @see AbstructJmsClient.class#attachListener(String)
	 */
	@Override
	public void attachListener(final String ctrlName) throws Exception {
		if (engine == null) {
			logger.error("CaoSQLEngine is not generated. Please execute init().");
			throw new NullComInterfaceException();
		}

		CaoSQLController ctrl = engine.Controller(ctrlName);
		ctrlMap.put(ctrlName, ctrl);
		final String delimiter = config.getString(KEY_TOPIC_DELIMITER);
		final String temp = config.getString(KEY_TOPIC_PREFIX);
		final String prefix = (temp != null && !temp.trim().isEmpty()) ? temp + delimiter : "";

		ctrl.attachListener(new CaoSQLControllerListener() {
			public void onChangeItem(CaoSQLItem pIitem) {
				producer.sendMessage(prefix + "OnChangeItem" + delimiter + ctrlName, pIitem);
				pIitem.release();
			}

			public void onChangeState(long state) {
				producer.sendMessage(prefix + "OnChangeState" + delimiter + ctrlName, state);
			}
		});

		ctrl.attachListenerEx(new CaoSQLControllerListenerEx() {
			public void onTrigger(long arg0) throws RemoteException {
				// 廃止予定メソッドなので何も実装しない
			}

			public void onMessage(CaoSQLMessage pMessage) throws UnsupportedTypeException, RemoteException {
				producer.sendMessage(prefix + "OnMessage" + delimiter + ctrlName, pMessage);
			}
		});
	}

	/**
	 * @see AbstructJmsClient.class#detatchListener(String)
	 */
	@Override
	public void detatchListener(final String ctrlName) throws Exception {
		if (ctrlMap == null || !ctrlMap.containsKey(ctrlName)) {
			logger.info("There is no controller monitoring the event.");
			return;
		}

		CaoSQLController ctrl = ctrlMap.get(ctrlName);
		ctrlMap.remove(ctrlName);
		if (ctrl != null) {
			ctrl.detachListener();
			ctrl.release();
			ctrl = null;
			logger.debug("{} is released.", ctrlName);
		}
	}
}
