/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JCRCtrlTnInfoImpl
*   ソースファイル名：JCRCtrlTnInfoImpl.java
*   作成者          ：富士通
*   日付            ：2012年03月07日
*＜機能概要＞
*   宅内機器連携（本番向け）コマンド発行部品です。
*＜修正履歴＞
*   バージョン  修正日       修正者      修正内容
*   v1.00.00    2012/03/07   FJ) 富士通  新規作成
*   v4.00.00    2013/01/11   FJ) 古内    IT1-2013-0000037
*   v68.00.00   2023/11/22   FJ) 三角    ANK-4463-00-00_【eo定期】サービスサーバリプレース対応_ＳT工程まで
*   v68.00.00   2024/01/24   FJ) 広田    ANK-4463-00-00_【eo定期】サービスサーバリプレース対応_ＳT工程まで
**********************************************************************/
package eo.ejb.common;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.fujitsu.futurity.model.ejb.common.JSYejbLog;
import com.fujitsu.futurity.model.ejb.common.fw.AgentDispatchContext;

import eo.common.constant.JCRStrConst;
import eo.common.util.JCRUtilCommon;
import eo.ejb.cbs.cbsmsg.ECRA0010001CBSMsg;
import eo.ejb.cbs.cbsmsg.ECRA0010001ConditionList;

/**
 * 宅内機器一意データ引継処理部品（本番）クラスです。
 * @author 富士通
 */
public class JCRCtrlTnInfoImpl extends JCRCtrlTnInfo
{
	
	/**
	 * インスタンス
	 */
	private static JCRCtrlTnInfoImpl instance = null;

	/**
	 * XML作成時のエンコーディング
	 */
	private static final String ENCODING = "UTF-8";

	/**
	 * 正常レスポンスコード
	 */
	private static final String OK_RESPONSE_RETURN_CODE = "0000";

	/**
	 * SOALFAULT（処理例外）
	 */
	private static final String SOAP_FAULT = "fault";

	/**
	 * SOALFAULT（エラーコード）
	 */
	private static final String SOAP_FAULT_CODE = "reason";

	/**
	 * SOALFAULT（エラー詳細）
	 */
	private static final String SOAP_FAULT_STRING = "detail";

	/**
	 * CRA0001
	 */
//ANK-4463-00-00 MOD START
	//private static final String ENDPOINT_CRA0001 = "taku_fsekikr0060";
	private static final String ENDPOINT_CRA0001 = "FSEKIKR0060";
//ANK-4463-00-00 MOD END

	/**
	 * 外部インターフェイスID
	 */
	private static final String OUT_IF_ID = "CRIFE027";

//ANK-4463-00-00 MOD START
	// HTTP関連定数定義
	/**
	 * HTTPヘッダ Content-TYPE
	 */
	private static final String CONTENT_TYPE = "Content-Type";
	
	/**
	 * HTTPヘッダ Conten-TYPE（値）
	 */
	private static final String CONTENT_TYPE_VALUE = "text/xml; charset=UTF-8";
//ANK-4463-00-00 MOD END

	/**
	 * コンストラクタです。
	 */
	private JCRCtrlTnInfoImpl()
	{
	}

	/**
	 * インスタンス取得用のメソッド
	 * 
	 * @return JCRCtrlTnInfoImpl
	 */
	public static synchronized JCRCtrlTnInfoImpl getInstance()
	{
		if(instance == null) 
		{
			instance = new JCRCtrlTnInfoImpl();
		}
		return instance;
	}

	private AgentDispatchContext inContext;
	public void setInContext(AgentDispatchContext inContext) {
		this.inContext = inContext;
	}

	private Connection connection = null;
	public void setConnection(Connection con) {
		this.connection = con;
		
	}

	/**
	 * コマンド発行部品
	 * 
	 * @param serviceMap サービスマップハッシュテーブル
	 * @return サービスマップ戻り値
	 * @throws Exception
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Map ctrlTnInfo_CRA0001(Map serviceMap)
	{
		Hashtable<String, Object> ret = new Hashtable<String, Object>();
		HttpURLConnection conURL = null;
		InputStream is = null;
		InetAddress inetaddress = null;
		Transformer transformer = null;
		OutputStream os = null;

		try
		{
			// リクエストXML作成
			Document soapRequest = createXml(serviceMap);
	
			// URLをプロパティファイルから取得
			String url = (String)JCCModelCommon.getApplicationConst(JCRStrConst.TN_URL) + ENDPOINT_CRA0001;
	
			// 宅内サーバへ接続
			URL takunaiServer = new URL(url);
			conURL = (HttpURLConnection)takunaiServer.openConnection();
			conURL.setRequestMethod("POST");
//ANK-4463-00-00 MOD START
			conURL.setRequestProperty(JCRCtrlTnInfoImpl.CONTENT_TYPE, JCRCtrlTnInfoImpl.CONTENT_TYPE_VALUE);
//ANK-4463-00-00 MOD END
			conURL.setDoOutput(true);
	
			// 宅内サーバへSOAPリクエストを送信
			os = conURL.getOutputStream();
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.ENCODING, ENCODING);//エンコーディング
			transformer.setOutputProperty(OutputKeys.METHOD, "xml");//XML形式
			transformer.transform(new DOMSource(soapRequest), new StreamResult(os)); 
			
			// 上り電文のログ出力
			StringWriter sw = new StringWriter();
			transformer.transform(new DOMSource(soapRequest), new StreamResult(sw));
			JSYejbLog.outlog(inContext, JSYejbLog.DEBUG, this.getClass(), sw.toString());
			
			inetaddress = InetAddress.getLocalHost();

			// 宅内サーバからSOAPレスポンスを受信（同期処理なので宅内からの応答を待つ）
			is = conURL.getInputStream();
			DOMResult dr = new DOMResult();
			transformer.transform(new StreamSource(is), dr);
			Node node = dr.getNode();

			ret = read(node);
			JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), ret);

			return ret;
		}
		catch(MalformedURLException e) 
		{
			ret.put(JCRStrConst.CMD_RESULT_CD, "1");
			ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
			return ret;
		}
		catch(ProtocolException e) 
		{
			ret.put(JCRStrConst.CMD_RESULT_CD, "1");
			ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
			return ret;
		}
		catch(UnknownHostException e) 
		{
			ret.put(JCRStrConst.CMD_RESULT_CD, "1");
			ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
			return ret;
		}
		catch (IOException e) 
		{
			if(conURL != null && inetaddress != null)
			{
				is = conURL.getErrorStream();
				try 
				{
					if(is != null)
					{
						DOMResult dr = new DOMResult();
						transformer.transform(new StreamSource(is), dr);
						Node node = dr.getNode();
						ret = read(node);
					}
					else 
					{
						ret.put(JCRStrConst.CMD_RESULT_CD, "1");
						ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
					}
				}
				catch(Exception ex) 
				{
					ret.put(JCRStrConst.CMD_RESULT_CD, "1");
					ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + ex.getMessage());
					return ret;
				}
			}
			else
			{
				ret.put(JCRStrConst.CMD_RESULT_CD, "1");
				ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
			}
			return ret;
		}
		catch(TransformerException e) 
		{
			ret.put(JCRStrConst.CMD_RESULT_CD, "1");
			ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
			return ret;
		}
		catch(Exception e) 
		{
			ret.put(JCRStrConst.CMD_RESULT_CD, "1");
			ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
			return ret;
			
		}
		finally
		{
			if (os != null)
			{
				try
				{
					os.close();
				}
				catch(Exception e) 
				{
					ret.put(JCRStrConst.CMD_RESULT_CD, "1");
					ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
					return ret;
				}
			}
			if (is != null)
			{
				try
				{
					is.close();
				}
				catch(Exception e) 
				{
					ret.put(JCRStrConst.CMD_RESULT_CD, "1");
					ret.put(JCRStrConst.CMD_ERROR_MESSAGE, "XML電文処理でエラーが発生しました。" + e.getMessage());
					return ret;
				}
			}
		}
	}

	/**
	 * リクエストXMLDocumentの作成
	 * 
	 * @param serviceMap サービスマップハッシュテーブル
	 * @return リクエストで送信するXMLドキュメント
	 * @throws Exception
	 */
	private Document createXml(Map serviceMap) throws Exception
	{
		// root要素が"<SOAP-ENV:Envelope>"のドキュメント
		DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = dbfactory.newDocumentBuilder();
		Document doc = builder.getDOMImplementation().createDocument("http://schemas.xmlsoap.org/soap/envelope/", "SOAP-ENV:Envelope", null);
		Element soap = doc.getDocumentElement();

		// ヘッダ部
		Element header = doc.createElement("SOAP-ENV:Header");
		soap.appendChild(header);
		Element requestId = doc.createElement("requestID");
		requestId.setTextContent(OUT_IF_ID + "_reqId" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));
		header.appendChild(requestId);
		Element serviceId = doc.createElement("serviceID");
		serviceId.setTextContent(serviceMap.get(JCRCtrlTnInfo.SERVICEID).toString());
		header.appendChild(serviceId);
		Element channel = doc.createElement("channel");
		channel.setTextContent("3");
		header.appendChild(channel);
		Element viewId = doc.createElement("viewID");
		viewId.setTextContent("CLA1");
		header.appendChild(viewId);
		Element operatorId = doc.createElement("operatorID");
		operatorId.setTextContent(serviceMap.get(JCRCtrlTnInfo.OPERATORID).toString());
		header.appendChild(operatorId);
		Element ipAddress = doc.createElement("ipAddress");
		ipAddress.setTextContent("");
		header.appendChild(ipAddress);
		Element operateDatetime = doc.createElement("operateDatetime");
		operateDatetime.setTextContent(serviceMap.get(JCRCtrlTnInfo.OPERATEDATETIME).toString());
		header.appendChild(operateDatetime);
		Element errorLevel = doc.createElement("errorLevel");
		errorLevel.setTextContent("");
		header.appendChild(errorLevel);
		Element returnCode = doc.createElement("returnCode");
		returnCode.setTextContent("");
		header.appendChild(returnCode);
		Element returnMessage = doc.createElement("returnMessage");
		returnMessage.setTextContent("");
		header.appendChild(returnMessage);
		
		// ボディ部
		Element body = doc.createElement("SOAP-ENV:Body");
		soap.appendChild(body);
		for (Map arg : (List<Map>)(serviceMap.get(ECRA0010001CBSMsg.ECRA0010001CONDITIONLIST)))
		{
			String str_tk_mdl_cd = (String)arg.get(ECRA0010001ConditionList.KEY_TK_MDL_CD);
			String str_kk_seizo_no = (String)arg.get(ECRA0010001ConditionList.KEY_KK_SEIZO_NO);
			String str_stbid = (String)arg.get(ECRA0010001ConditionList.KEY_STBID);
			String str_tk_shokai_sbt_cd = (String)arg.get(ECRA0010001ConditionList.KEY_TK_SHOKAI_SBT_CD);
			
			Element record = doc.createElement("record");
			body.appendChild(record);
			Element condition = doc.createElement("condition");
			record.appendChild(condition);
			Element tk_mdl_cd = doc.createElement("TK_MDL_CD");
			tk_mdl_cd.setTextContent(str_tk_mdl_cd);
			condition.appendChild(tk_mdl_cd);
			Element kk_seizo_no = doc.createElement("KK_SEIZO_NO");
			kk_seizo_no.setTextContent(str_kk_seizo_no);
			condition.appendChild(kk_seizo_no);
			Element stbid = doc.createElement("STBID");
			stbid.setTextContent(str_stbid);
			condition.appendChild(stbid);
			Element tk_shokai_sbt_cd = doc.createElement("TK_SHOKAI_SBT_CD");
			tk_shokai_sbt_cd.setTextContent(str_tk_shokai_sbt_cd);
			condition.appendChild(tk_shokai_sbt_cd);
		}

		// スタンドアロンの設定
		doc.setXmlStandalone(true);

		return doc;
	}

	/**
	 * 電文読み込み処理
	 * 
	 * @param node
	 * @return XML電文解析結果
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 */
	private Hashtable<String, Object> read(Node node) 
	throws IOException,ParserConfigurationException,SAXException
	{
		Hashtable<String, Object> ret = new Hashtable<String, Object>();

			if(node instanceof Document)
			{
				Document soapResponse = (Document)node;

				// レスポンス解析
				Element root =  soapResponse.getDocumentElement();//ルート要素(<SOAP-ENV:Envelope>)

				// SOAP例外の場合
				if(root.getElementsByTagName(SOAP_FAULT).item(0) != null)
				{
					String faultinfo = analyzeFaultXml(node);
					ret.put(JCRStrConst.CMD_RESULT_CD, "1");
					ret.put(JCRStrConst.CMD_ERROR_MESSAGE, faultinfo);	
					return ret;
				}
				else
				{
					//リターン値取得
					String elValue = getTextContext(root, "errorLevel", 0);
					ret.put(JCRCtrlTnInfo.ERROR_LEVEL, elValue);
					String rcValue = getTextContext(root, "returnCode", 0);
					ret.put(JCRCtrlTnInfo.RETURN_CD, rcValue);
					String rmValue = getTextContext(root, "returnMessage", 0);
					ret.put(JCRCtrlTnInfo.RETURN_MESSAGE, rmValue);
	
					ret.put(JCRStrConst.CMD_RESULT_CD, "0");
	
					// エラーチェック
					if(OK_RESPONSE_RETURN_CODE.equals(rcValue) == false )
					{
						// 異常ケース
						ArrayList<Map<String, Object>> list = handleResponseXMLError(root);
						ret.put(JCRCtrlTnInfo.ECRA0010001CONDITIONLIST, list);
					}
					else
					{
						// 正常ケース
						ArrayList<Map<String, Object>> list = handleResponseXML(root);
						ret.put(JCRCtrlTnInfo.ECRA0010001CBSMSG1LIST, list);
					}
				}
			}

		return ret;
	}

	/**
	 * レスポンスXMLDocumentの解析（正常）
	 * 
	 * @param root ルート要素
	 * @return レスポンスXMLドキュメントの解析結果のリスト
	 */
	private ArrayList<Map<String, Object>> handleResponseXML(Element root)
	{
		ArrayList<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

		//soapの子要素recordを取得
		NodeList recordList = root.getElementsByTagName("record");
		//recordの要素数分まわす
		for(int i = 0; i < recordList.getLength(); i++)
		{
			Hashtable<String, Object> data = new Hashtable<String, Object>();
			Element record = (Element)recordList.item(i);
			// 宅内機器型式コード
			String text = getTextContext(record, "TK_MDL_CD", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.TK_MDL_CD, text);
			}
			// 機器製造番号
			text = getTextContext(record, "KK_SEIZO_NO", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.KK_SEIZO_NO, text);
			}
			// 宅内機器ステータス
			text = getTextContext(record, "TK_STAT", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.TK_STAT, text);
			}
			// 所有コード
			text = getTextContext(record, "SHOYU_CD", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.SHOYU_CD, text);
			}
			// 管理場所識別コード
			text = getTextContext(record, "KNRI_PLC_SKBT_CD", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.KNRI_PLC_SKBT_CD, text);
			}
			// 管理場所コード
			text = getTextContext(record, "KNRI_PLC_CD", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.KNRI_PLC_CD, text);
			}
			// 管理場所棚コード
			text = getTextContext(record, "KNRI_PLC_SLF_CD", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.KNRI_PLC_SLF_CD, text);
			}
			// 宅内機器基本T更新日付
			text = getTextContext(record, "KIHON_UPD_DTTM", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.KIHON_UPD_DTTM, text);
			}
			// STBID
			text = getTextContext(record, "STBID", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.STBID, text);
			}
			// 宅内機器STBT更新日付
			text = getTextContext(record, "STBT_UPD_DTTM", 0);
			if(!JCRUtilCommon.isNull(text))
			{
				data.put(JCRCtrlTnInfo.STBT_UPD_DTTM, text);
			}
			list.add(data);
			JSYejbLog.outlog(inContext, JSYejbLog.EXECUTION , this.getClass(), "record" + ":" + data);
		}

		return list;
	}

	/**
	 * レスポンスXMLDocumentの解析（エラー）
	 * 
	 * @param root ルート要素
	 * @return レスポンスXMLドキュメントの解析結果のリスト
	 */
	private ArrayList<Map<String, Object>> handleResponseXMLError(Element root)
	{
		ArrayList<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

		//soapの子要素recordを取得
		NodeList recordList = root.getElementsByTagName("record");
		//recordの要素数分まわす
		for(int i = 0; i < recordList.getLength(); i++)
		{
			Hashtable<String, Object> data = new Hashtable<String, Object>();
			Element record = (Element)recordList.item(i);
			// 宅内機器型式コード
			String errCd = getErrCd(record, "TK_MDL_CD", 0);
			if(!JCRUtilCommon.isNull(errCd))
			{
				data.put(ECRA0010001ConditionList.KEY_TK_MDL_CD_ERR, errCd);
			}
			// 機器製造番号
			errCd = getErrCd(record, "KK_SEIZO_NO", 0);
			if(!JCRUtilCommon.isNull(errCd))
			{
				data.put(ECRA0010001ConditionList.KEY_KK_SEIZO_NO_ERR, errCd);
			}
			// STBID
			errCd = getErrCd(record, "STBID", 0);
			if(!JCRUtilCommon.isNull(errCd))
			{
				data.put(ECRA0010001ConditionList.KEY_STBID_ERR, errCd);
			}
			// 宅内機器照会種別コード
			errCd = getErrCd(record, "TK_SHOKAI_SBT_CD", 0);
			if(!JCRUtilCommon.isNull(errCd))
			{
				data.put(ECRA0010001ConditionList.KEY_TK_SHOKAI_SBT_CD_ERR, errCd);
			}
			list.add(data);
			JSYejbLog.outlog(inContext, JSYejbLog.EXECUTION , this.getClass(), "record" + ":" + data);
		}

		return list;
	}

	/**
	 * XMLドキュメントの要素の値を取得
	 * 
	 * @param element ルート要素
	 * @param value 要素名
	 * @param i 要素番号
	 * @return 取得した要素の値
	 */
	private String getTextContext(Element element, String value, int i)
	{
		Node node = element.getElementsByTagName(value).item(i);
		String textCon = node.getTextContent();
		return textCon;
	}

	/**
	 * XMLFAULT電文解析処理
	 * 
	 * @param elem XMLFAULT電文
	 * @return ArrayListエラー情報
	 */
	private String analyzeFaultXml(Node elem) 
	{
		Node node = elem.getFirstChild();
		String errmsg = "";
		
		while(node != null)
		{
			
			if(node.getNodeType() == Node.ELEMENT_NODE) 
			{
				String nodename = node.getLocalName();

				if(JCRCtrlTnInfoImpl.SOAP_FAULT_CODE.equals(nodename.toLowerCase()))
				{
					Node child = node.getFirstChild();
					if(child != null)
					{
						errmsg = errmsg + "エラーコード：" + child.getTextContent();
					}
				}
				if(JCRCtrlTnInfoImpl.SOAP_FAULT_STRING.equals(nodename.toLowerCase()))
				{
					errmsg = errmsg + "エラー詳細：" + node.getTextContent();
				}
			}
			
			node = node.getNextSibling();
		}
		
		return errmsg;
	}

	/**
	 * エラーコード取得
	 * 
	 * @param element ルート要素
	 * @param value 要素名
	 * @param i 要素番号
	 * @return エラーコード
	 */
	private String getErrCd(Element element, String value, int i)
	{
		String errCd = null;
		Node node = element.getElementsByTagName(value).item(i);

		if(node.getAttributes().getNamedItem("err") != null)
		{
			String StrErr = node.getAttributes().getNamedItem("err").getTextContent();
			int intErr = Integer.parseInt(StrErr.substring(1));
			if(intErr == 1)
			{
				errCd = "E1";
			}
			else if(intErr <= 52)
			{
				errCd = "E2";
			}
			else if(intErr <= 99)
			{
				errCd = "E3";
			}
			else
			{
				errCd = "EA";
			}
			JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), value + " err=" + StrErr);
		}

		return errCd;
	}
}
