using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using CommonMP.HYSSOP.Interface.HSData;
using CommonMP.HYSSOP.CoreImpl.HSData;
using CommonMP.HYSSOP.CoreImpl.HSTools;
using CommonMP.HYSSOP.CoreImpl.HSDBA;
using CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase;

namespace CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase
{
    /// <summary><para>class outline:</para>
    /// <para>演算結果データDBAクラス</para>
    /// </summary>
    /// <remarks><para>remarks:</para>
    /// <para>演算結果データDBへのアクセスを提供する。</para>
    /// <para>本クラスは共通形式ファイル実装である。</para>
    /// <para>DBパス、ファイル名規則に関わる部分のみオーバーライドする。</para>
    /// </remarks>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2009/06/22][新規作成]</para>
    /// </remarks>
    public class HySCalResultDataDBASub : HySCommonFileDBA
    {
        /// <summary>
        /// 自クラス名(ログ出力用)
        /// </summary>
        private const string m_csMyClassName = "HySCalResultDataDBASub";

        /// <summary>
        /// DBフォルダパス(ホームディレクトリからの相対パス)
        /// </summary>
        private string m_csDBPath = null;

        /// <summary><para>method outline:</para>
        /// <para>デフォルトコンストラクタ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySCommonDBA csDBA = new HySCalResultDataDBASub();</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>HySCalResultDataDBASub 生成されたクラスのインスタンス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>使用禁止</para>
        /// </remarks>
        private HySCalResultDataDBASub()
        {
        }

        /// <summary><para>method outline:</para>
        /// <para>コンストラクタ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySCommonDBA csDBA = new HySCalResultDataDBASub(csDBPath);</para>
        /// </example>
        /// <param name="csDBPath">DBフォルダ(ホームディレクトリからの相対パス)</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public HySCalResultDataDBASub(string csDBPath)
            : base()
        {
            m_csDBPath = csDBPath;
        }

        #region カスタマイズパラメータ

        /// <summary>
        /// データファイルプレフィックス
        /// </summary>
        public const string DATAFILE_PREFFIX = "CRes_";

        /// <summary>
        /// データファイル拡張子
        /// </summary>
        public const string DATAFILE_EXTENSION = ".dat";
        
        /// <summary>
        /// メタデータリストファイル名
        /// </summary>
        public const string METADATALIST_FILENAME = "CalResultData.mng";

        /// <summary>
        /// メタデータリスト一時ファイル名
        /// </summary>
        public const string TMP_METADATALIST_FILENAME = "TmpCalResultData.mng";

        /// <summary>
        /// メタデータリストダンプファイル名
        /// </summary>
        public const string METADATALIST_DUMP_FILENAME = "CalResultData.mng.txt";

        #endregion

        #region テンプレートメソッド

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）データファイルの拡張子を取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csExt = GetDataFileExtension() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>演算ロット情報データファイルの拡張子</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override string GetDataFileExtension()
        {
            return DATAFILE_EXTENSION;
        }

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）データファイルのプレフィックスを取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csPreffix = GetDataFilePreffix() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>演算ロット情報データファイルのプレフィックス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override string GetDataFilePreffix()
        {
            return DATAFILE_PREFFIX;
        }

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）メタデータリストファイル名を取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csFileName = GetMetaDataListFileName() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>メタデータリストファイル名</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override string GetMetaDataListFileName()
        {
            return METADATALIST_FILENAME;
        }

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）メタデータリスト一時ファイル名を取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csFileName = GetTmpMetaDataListFileName() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>メタデータリスト一時ファイル名</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override string GetTmpMetaDataListFileName()
        {
            return TMP_METADATALIST_FILENAME;
        }

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）メタデータリストダンプファイル名を取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csFileName = GetMetaDataListDumpFileName() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>メタデータリストダンプファイル名</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override string GetMetaDataListDumpFileName()
        {
            return METADATALIST_DUMP_FILENAME;
        }

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）DBパスを取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csPath = GetDBBasePath() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>DBパス名</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override string GetDBBasePath()
        {
            return m_csDBPath;
        }

        /// <summary><para>method outline:</para>
        /// <para>（オーバーライド）検索条件準備</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csPath = PrepareQuery(csQueryCtlData, ref csComparerList) </para>
        /// </example>
        /// <param name="csQueryCtlData">検索条件</param>
        /// <param name="csComparerList">フィールド値比較デリゲートリスト</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override void PrepareQuery(HySQueryCtlData csQueryCtlData, ref List<HySDBAFieldComparer> csComparerList)
        {
            // 基本部分の検索条件を初期化する
            base.PrepareQuery(csQueryCtlData, ref csComparerList);

            // 演算結果データ固有の検索条件を初期化する
            HySQueryEntryData csQueryEntryData;
            // PROJECT_NAME
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.PROJECT_NAME)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.PROJECT_NAME, csStockData);
                }));
            }
            // MANAGER_NAME
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.MANAGER_NAME)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.MANAGER_NAME, csStockData);
                }));
            }
            // MODEL_TYPE
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.MODEL_TYPE)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.MODEL_TYPE, csStockData);
                }));
            }
            // MODEL_TARGET
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.MODEL_TARGET)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.MODEL_TARGET, csStockData);
                }));
            }
            // MODEL_CALC_TYPE
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.MODEL_CALC_TYPE)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.MODEL_CALC_TYPE, csStockData);
                }));
            }
            // MODEL_SUBJECT
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.MODEL_SUBJECT)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.MODEL_SUBJECT, csStockData);
                }));
            }
            // MODEL_SITUATION
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.MODEL_SITUATION)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.MODEL_SITUATION, csStockData);
                }));
            }
            // CALC_EXEC_DATE
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.CALC_EXEC_DATE)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.CALC_EXEC_DATE, csStockData);
                }));
            }
            // ELEMENT_ID
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.ELEMENT_ID)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.ELEMENT_ID, csStockData);
                }));
            }
            // CALC_RESULT_ID
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultMetaKeyDefines.CALC_RESULT_ID)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultMetaKeyDefines.CALC_RESULT_ID, csStockData);
                }));
            }
            // ELEMENT_NAME
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.ELEMENT_NAME)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.ELEMENT_NAME, csStockData);
                }));
            }
            // DATA_TYPE
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.DATA_TYPE)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.DATA_TYPE, csStockData);
                }));
            }
            // DATA_DIM1
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.DATA_DIM1)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.DATA_DIM1, csStockData);
                }));
            }
            // DATA_DIM2
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.DATA_DIM2)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.DATA_DIM2, csStockData);
                }));
            }
            // DATA_DIM3
            if ((csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.DATA_DIM3)) != null)
            {
                csComparerList.Add(new HySDBAFieldComparer(csQueryEntryData, delegate(HySStockData csStockData)
                {
                    return GetKeyInfoFieldValue(HySCalResultDataQueryFieldNames.DATA_DIM3, csStockData);
                }));
            }
            
        }


        /// <summary><para>method outline:</para>
        /// <para>新規登録時の追加重複チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = AdditionalDuplicateCheck(csMetaDataList, csStockData) </para>
        /// </example>
        /// <param name="csMetaDataList">メタデータリスト</param>
        /// <param name="csStockData">登録対象のDB保存データ</param>
        /// <returns>true:登録可能(重複なし)、false:登録不可(重複あり)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>論理ユニークキーの重複チェックを行う。比較項目は演算結果IDと演算結果連番。</para>
        /// </remarks>
        protected override bool AdditionalDuplicateCheck(HySStockMetaDataList csMetaDataList, HySStockData csStockData)
        {
            const string csMyMethodName = "AdditionalDuplicateCheck";

            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);

            HySID csCalResultID = (HySID)csKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_ID);
            HySLong csSeqNO = (HySLong)csKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_SEQNO);
            if (csSeqNO == null)
            {
                // UPDATE時は連番を設定しないのでチェック不要
                return true;
            }

            foreach (HySStockMetaData csMetaData in csMetaDataList)
            {
                HySStockData csDBStockData = csMetaData.GetStockData();
                HySDataHashTable csDBKeyInfo = csDBStockData.GetKeyInfo(ref lKeyNumber);

                HySID csDBCalResultID = (HySID)csDBKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_ID);
                HySLong csDBSeqNO = (HySLong)csDBKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_SEQNO);

                if (csDBCalResultID.Equals(csCalResultID) && csDBSeqNO.m_lValue == csSeqNO.m_lValue)
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "data duplicate",
                        "calc-result-id", csCalResultID,
                        "seq-no", csSeqNO);
                    return false;
                }
            }
            return true;
        }

        #endregion

        #region privateメソッド

        /// <summary><para>method outline:</para>
        /// <para>追加検索キーの値取り出し</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySDataRoot csValue = GetKeyInfoFieldValue(csFieldName, csStockData) </para>
        /// </example>
        /// <param name="csFieldName">キーとなるフィールド名</param>
        /// <param name="csStockData">取り出し元のDB保存データ</param>
        /// <returns>追加検索キーの値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static HySDataRoot GetKeyInfoFieldValue(HySString csFieldName, HySStockData csStockData)
        {
            long lKeyNumber = 0;
            HySDataHashTable csHash = csStockData.GetKeyInfo(ref lKeyNumber);
            if (lKeyNumber > 0)
            {
                return csHash[csFieldName];
            }
            else
            {
                return null;
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>検索条件からサブフォルダパス取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = GetSubFolderListFromQuery(csQuery, csSubFolderPaths) </para>
        /// </example>
        /// <param name="csQuery">検索条件</param>
        /// <param name="csSubFolderPaths">サブフォルダパス(相対パス)</param>
        /// <returns>true:成功、false:失敗(不正なデータ)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private bool GetSubFolderListFromQuery(HySQueryCtlData csQuery, out string[] csSubFolderPaths)
        {
            const string csMyMethodName = "GetSubFolderListFromQuery";
            csSubFolderPaths = null;

            List<string> csDataKindList = new List<string>();
            List<string> csElementIDList = new List<string>();

            HySQueryEntryData csQEntry;

            // 検索条件からデータ種別を取得する
            csQEntry = csQuery.GetQueryEntryData(HySCalResultDataQueryFieldNames.DATA_KIND);
            if (csQEntry != null)
            {
                HySQueryCompareMode eCompareMode = csQEntry.GetCompareMode();
                if (eCompareMode == HySQueryCompareMode.EQUALS)
                {
                    csDataKindList.Add(((HySObjectKind)csQEntry.GetValueToCompare()).ToString());
                }
                else if (eCompareMode == HySQueryCompareMode.IN)
                {
                    HySObjectKind[] csIDs = (HySObjectKind[])csQEntry.GetValueToCompare();
                    foreach (HySObjectKind csID in csIDs)
                    {
                        csDataKindList.Add(csID.ToString());
                    }
                }
                else if (eCompareMode == HySQueryCompareMode.NOT)
                {
                    // 全フォルダを探索する必要があるので何もしない
                }
                else
                {
                    // 非サポートの組み合わせ
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "unsupported query",
                        "FieldName", csQEntry.GetFieldName(),
                        "CompareMode", csQEntry.GetCompareMode());
                    return false;
                }
            }
            // 検索条件から要素IDを取得する
            csQEntry = csQuery.GetQueryEntryData(HySCalResultDataQueryFieldNames.ELEMENT_ID);
            if (csQEntry != null)
            {
                HySQueryCompareMode eCompareMode = csQEntry.GetCompareMode();
                if (eCompareMode == HySQueryCompareMode.EQUALS)
                {
                    csElementIDList.Add(((HySID)csQEntry.GetValueToCompare()).ToString());
                }
                else if (eCompareMode == HySQueryCompareMode.IN)
                {
                    HySID[] csIDs = (HySID[])csQEntry.GetValueToCompare();
                    foreach (HySID csID in csIDs)
                    {
                        csElementIDList.Add(csID.ToString());
                    }
                }
                else if (eCompareMode == HySQueryCompareMode.NOT)
                {
                    // 全フォルダを探索する必要があるので何もしない
                }
                else
                {
                    // 非サポートの組み合わせ
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "unsupported query",
                        "FieldName", csQEntry.GetFieldName(),
                        "CompareMode", csQEntry.GetCompareMode());
                    return false;
                }
            }

            // データ種別、要素IDからフォルダパスを組み立てる
            if (csDataKindList.Count == 0)
            {
                csDataKindList.Add(string.Empty);
            }
            if (csElementIDList.Count == 0)
            {
                csElementIDList.Add(string.Empty);
            }
            DirectoryInfo csDBBaseDirInfo = new DirectoryInfo(m_csDBPath); // DB基準フォルダのDirectoryInfo
            Dictionary<string, string> csSubFolderPathDict = new Dictionary<string, string>();  // サブフォルダ名辞書(重複除去用)
            foreach (string csDataKind in csDataKindList)
            {
                foreach (string csElementID in csElementIDList)
                {
                    // 検索パターンを生成する(ワイルドカードを含む場合がある)
                    string csPattern = HySFileRecordIDUtility.CreateIDWildCardPattern(csDataKind, csElementID);
                    // サブディレクトリを検索する
                    DirectoryInfo[] csSubDirInfos = csDBBaseDirInfo.GetDirectories(csPattern, SearchOption.TopDirectoryOnly);
                    // 見つかったものを辞書に入れる
                    foreach (DirectoryInfo csSubDirInfo in csSubDirInfos)
                    {
                        string csFolderName = csSubDirInfo.Name;
                        if (!csSubFolderPathDict.ContainsKey(csFolderName))
                        {
                            csSubFolderPathDict.Add(csFolderName, csFolderName);
                        }
                    }
                }
            }
            // 出力パラメータ設定
            csSubFolderPaths = new string[csSubFolderPathDict.Count];
            int i = 0;
            foreach (string csSubFolderPath in csSubFolderPathDict.Keys)
            {
                csSubFolderPaths[i++] = csSubFolderPath;
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>全てのサブフォルダパス取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string[] path = GetAllSubFolderPath() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>サブフォルダパス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string[] GetAllSubFolderPath()
        {
            // DB基準パス配下のディレクトリ一覧を収集する
            List<string> csSubFolderPathList = new List<string>();
            DirectoryInfo csDBBaseDirInfo = new DirectoryInfo(m_csDBPath);
            foreach (DirectoryInfo csSubDirInfo in csDBBaseDirInfo.GetDirectories("*", SearchOption.TopDirectoryOnly))
            {
                csSubFolderPathList.Add(csSubDirInfo.Name);
            }
            return csSubFolderPathList.ToArray();
        }

        #endregion
    }
}
