// <summary>ソースコード：横断面データファイルクラス</summary>
// <author>CommonMP</author>
using System;
using System.Collections.Generic;
using System.Text;

using CommonMP.HYSSOP.CoreImpl.HSData;

namespace CommonMP.HYSSOP.CoreImpl.HSTools
{
    /// <summary><para>class outline:</para>
    /// <para>横断面データファイルクラス</para>
    /// </summary>
    /// <remarks><para>remarks:</para>
    /// <para>複数の横断面データをファイル出力する機能を提供するクラス。</para>
    /// <para>ファイルフォーマットはCommonMP標準フォーマット(csv形式)とする。</para>
    /// </remarks>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2010/01/21][新規作成]</para>
    /// </remarks>
    public class HySGeoRiverCrossSectionDataFile
    {
        /// <summary>ヘッダ情報　属性 int</summary>
        public static readonly string ATTRIBUTE_INT = "int";
        /// <summary>ヘッダ情報　属性 double</summary>
        public static readonly string ATTRIBUTE_DOUBLE = "double";
        /// <summary>ヘッダ情報　属性 char</summary>
        public static readonly string ATTRIBUTE_CHAR = "char";
        /// <summary>ヘッダ情報　単位 メートル</summary>
        public static readonly string UNIT_METER = "m";
        /// <summary>ヘッダ情報　単位 標高</summary>
        public static readonly string UNIT_TPM = "T.P.m";
        /// <summary>ヘッダ情報　単位 単位無し</summary>
        public static readonly string UNIT_ND = "nd";
        /// <summary>データ部　ヘッダ</summary>
        public static readonly string DATA_HEADER = "[start]";
        /// <summary>データ部　フッタ</summary>
        public static readonly string DATA_FOOTER = "[end]";

        /// <summary>要素名称「河川名」</summary>
        public static readonly string ELEMENT_RIVER_NAME = "河川名";
        /// <summary>要素名称「topoID」</summary>
        public static readonly string ELEMENT_TOPO_ID = "topoID";
        /// <summary>要素名称「河川名」</summary>
        public static readonly string ELEMENT_CROSSSECTION_ID = "断面ID";
        /// <summary>要素名称「x座標」</summary>
        public static readonly string ELEMENT_X_VALUE = "x座標";
        /// <summary>要素名称「z座標」</summary>
        public static readonly string ELEMENT_Z_VALUE = "z座標";
        /// <summary>要素名称「パネル」</summary>
        public static readonly string ELEMENT_PANEL = "パネル";
        /// <summary>要素名称「粗度係数」</summary>
        public static readonly string ELEMENT_ROUGHNESS_COEFFIEINCT = "粗度係数";

        /// <summary>自クラス名(ログ出力用)</summary>
        private static readonly string CLASS_NAME = "HySGeoRiverCrossSectionDataFile";

        /// <summary>ファイルパス</summary>
        private HySString m_csFilePath = null;

        /// <summary><para>method outline:</para>
        /// <para>デフォルトコンストラクタ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> HySGeoRiverCrossSectionDataFile csSectionDataFile = new HySGeoRiverCrossSectionDataFile(csFilePath) </para>
        /// </example>
        /// <param name="csFilePath">ファイルパス</param>
        /// <returns>HySGeoRiverCrossSectionDataFile 生成したインスタンス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public HySGeoRiverCrossSectionDataFile(HySString csFilePath)
        {
            m_csFilePath = csFilePath;
        }

        /// <summary><para>method outline:</para>
        /// <para>書き込み処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Write(csData) </para>
        /// </example>
        /// <param name="csData">横断面データ配列</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>距離標の距離順にソートし、ファイル出力する</para>
        /// </remarks>
        public bool Write(HySGeoRiverCrossSectionData[] csData)
        {
            // 距離標の距離順にソートする
            IComparer<HySGeoRiverCrossSectionData> csDistanceComparer = new HySRiverCrossSectionDistanceComparer();
            Array.Sort(csData, csDistanceComparer);

            // csv出力するための行リスト格納用
            List<string[]> csRowList = new List<string[]>();

            // ファイルヘッダ部の追加
            csRowList.AddRange(CreateFileHeader());

            // 出力する横断面データ分のデータ行を追加
            foreach (HySGeoRiverCrossSectionData csCrossSectionData in csData)
            {
                csRowList.AddRange(CreateDataRowList(csCrossSectionData));
            }

            // ファイルに出力する
            if (!WriteCsvFile(csRowList))
            {
                return false;
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>csvファイル書き込み</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = WriteCsvFile(csRowList) </para>
        /// </example>
        /// <param name="csRowList">行リスト</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>各行の情報をカンマ区切りで結合して1行ずつファイル出力する</para>
        /// </remarks>
        private bool WriteCsvFile(List<string[]> csRowList)
        {
            const string csMethodName = "WriteCsvFile";
            
            HySFile csFile = new HySFile(m_csFilePath);
            if (csFile.Open
                (HySFile.OPEN_MODE.CREATE, HySFile.READ_WRITE_MODE.READ_WRITE,HySFile.DIRECTORY_MODE.MK_DIR) != 0)
            {
                // ファイルオープン失敗
                // ver1.5 エラートレース日本語対応
                string DispStatement = Properties.HysMsgResources.STATEMENT_FILE_OPEN_NG + string.Format( "(FilePath = [{0}] )",  m_csFilePath.ToString());
                HySLog.LogOut(HySLog.ONLINE, CLASS_NAME, csMethodName, DispStatement);

               // HySLog.LogOut(HySLog.ONLINE, CLASS_NAME, csMethodName, string.Format
               //                     ("ファイルのオープンに失敗しました。(FilePath = [{0}])", m_csFilePath.ToString()));
                return false;
            }

            // 行リストを1行分ずつ出力していく
            foreach (string[] csRow in csRowList)
            {
                string csValue = string.Join(",", csRow);
                if (csFile.WriteText(csValue) != 0)
                {
                    // 行出力エラー
                    // ver1.5 エラートレース日本語対応
                    string DispStatement = Properties.HysMsgResources.STATEMENT_TEXTOUT_NG + string.Format("(text = [{0}])", csValue );
                    HySLog.LogOut(HySLog.ONLINE, CLASS_NAME, csMethodName, DispStatement );

                   // HySLog.LogOut(HySLog.ONLINE, CLASS_NAME, csMethodName, string.Format
                   //                     ("行テキスト出力に失敗しました。(text = [{0}])", csValue));
                    // ファイル削除
                    csFile.Close();
                    csFile.Delete();
                    return false;
                }
            }

            // ファイル出力完了
            csFile.Close();

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>ファイルヘッダ生成</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> List csHeader = CreateFileHeader() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>横断面データファイルのヘッダ情報を作成する。</para>
        /// <para>ヘッダは「断面情報定義部」と「属性情報定義部」の各3行、計6行で構成される。</para>
        /// </remarks>
        private List<string[]> CreateFileHeader()
        {
            // 断面情報定義部(河川名要素、topoID要素、断面ID要素)を生成する
            HySGeoRiverCrossSectionDataFileHeaderElement[] csCrossSectionInfoElementList =
                new HySGeoRiverCrossSectionDataFileHeaderElement[]
                {
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_RIVER_NAME, ATTRIBUTE_CHAR, UNIT_ND),
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_TOPO_ID, ATTRIBUTE_CHAR, UNIT_ND),
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_CROSSSECTION_ID, ATTRIBUTE_CHAR, UNIT_ND)
                };

            List<string[]> csHeaderList = ConvHeaderElementListToRowList(csCrossSectionInfoElementList);

            // 属性情報定義部(x座標要素、z座標要素、パネル要素、粗度係数要素)を生成する
            HySGeoRiverCrossSectionDataFileHeaderElement[] csDataAttributeElementList =
                new HySGeoRiverCrossSectionDataFileHeaderElement[]
                {
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_X_VALUE, ATTRIBUTE_DOUBLE, UNIT_METER),
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_Z_VALUE, ATTRIBUTE_DOUBLE, UNIT_TPM),
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_PANEL, ATTRIBUTE_INT, UNIT_ND),
                    new HySGeoRiverCrossSectionDataFileHeaderElement( ELEMENT_ROUGHNESS_COEFFIEINCT, ATTRIBUTE_DOUBLE, UNIT_ND),
                };

            csHeaderList.AddRange(ConvHeaderElementListToRowList(csDataAttributeElementList));

            return csHeaderList;
        }

        /// <summary><para>method outline:</para>
        /// <para>ヘッダ要素リストから行リストを生成する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> List csRowList = ConvHeaderElementListToRowList(csElementList) </para>
        /// </example>
        /// <param name="csElementList">ヘッダ要素リスト</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>横断面データファイルのヘッダ要素は「名称」「属性」「単位」の3つの情報を持つ。</para>
        /// <para>ヘッダ要素リストから「名称行」「属性行」「単位行」の3つの行を生成し、リスト化して返却する。</para>
        /// </remarks>
        private List<string[]> ConvHeaderElementListToRowList(HySGeoRiverCrossSectionDataFileHeaderElement[] csElementList)
        {
            List<string[]> csList = new List<string[]>();
            List<string> csNameList = new List<string>();
            List<string> csAttributeList = new List<string>();
            List<string> csUnitList = new List<string>();

            // 各要素から「名称」「属性」「単位」を取り出してそれぞれのリストに追加する
            foreach (HySGeoRiverCrossSectionDataFileHeaderElement csElement in csElementList)
            {
                csNameList.Add(csElement.GetName());
                csAttributeList.Add(csElement.GetAttribute());
                csUnitList.Add(csElement.GetUnit());
            }

            // 行リストに各リストを設定
            csList.Add(csNameList.ToArray());
            csList.Add(csAttributeList.ToArray());
            csList.Add(csUnitList.ToArray());

            return csList;
        }

        /// <summary><para>method outline:</para>
        /// <para>データ行リスト生成</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> List csRowList = CreateDataRowList(csData) </para>
        /// </example>
        /// <param name="csData">横断面データ</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>横断面データからcsv出力するためのデータ行リストを生成する</para>
        /// </remarks>
        private List<string[]> CreateDataRowList(HySGeoRiverCrossSectionData csData)
        {
            List<string[]> csList = new List<string[]>();

            // 開始行･･･[start]
            csList.Add(new string[] { DATA_HEADER });

            // 2行目･･･断面情報(河川名, topoID, 断面ID[→距離標名])
            csList.Add(CreateDataHeaderInfoRow(csData));

            // 3行目～･･･属性情報(x座標, z座標, パネル, 粗度係数)
            HySGeoRiverCrossSectionShape csShape = csData.GetSectionShape();
            HySDataLinkedList csPointList = csShape.GetShapePoints();
            csPointList.SetCursorFirst();
            for (int i = 0; i < csPointList.GetCount(); i++, csPointList.MoveCursorNext())
            {
                HySGeoRiverCrossSectionShapePoint csPoint = (HySGeoRiverCrossSectionShapePoint)csPointList.GetCursorData();
                // 座標情報を文字列化して設定、パネルと粗度係数は空白とする。
                csList.Add(new string[] { csPoint.GetXValue().ToString("0.000"), csPoint.GetHValue().ToString("0.000"),
                                          string.Empty , string.Empty });
            }

            // 終了行･･･[end]
            csList.Add(new string[] { DATA_FOOTER });

            return csList;
        }

        /// <summary><para>method outline:</para>
        /// <para>データ断面情報行生成</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string[] csRow = CreateDataHeaderRow(csData) </para>
        /// </example>
        /// <param name="csData">横断面データ</param>
        /// <returns>string[] データ断面情報行</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>横断面データからデータ断面情報行(河川名, topoID, 断面ID[→距離標名])を作成する</para>
        /// </remarks>
        private string[] CreateDataHeaderInfoRow(HySGeoRiverCrossSectionData csData)
        {
            // 河川名と断面ID[→距離標名]はデータから取得できる場合はそれを設定する。
            // 設定されていなければ空白とする。
            string csRiverName = string.Empty;
            if(!IComparable.Equals(null, csData.GetRiverName()))
            {
                csRiverName = csData.GetRiverName().ToString();
            }

            string csCrossSectionID = string.Empty;
            if (!IComparable.Equals(null, csData.GetDistancePost()))
            {
                csCrossSectionID = csData.GetDistancePost().ToString();
            }

            // topoIDはデフォルト空白で出力する。
            return new string[] { csRiverName, string.Empty, csCrossSectionID };
        }

        /// <summary><para>class outline:</para>
        /// <para>横断面データファイルヘッダ要素クラス</para>
        /// </summary>
        /// <remarks><para>remarks:</para>
        /// <para>横断面データファイルのヘッダ情報に設定する要素を表すクラス。</para>
        /// <para>1つの要素は「名称」「属性」「単位」の組で表される。</para>
        /// </remarks>
        /// <remarks><para>history:</para>
        /// <para>[CommonMP][ver 1.0.0][2010/01/21][新規作成]</para>
        /// </remarks>
        public class HySGeoRiverCrossSectionDataFileHeaderElement
        {
            /// <summary>要素名称</summary>
            private string m_csName = null;
            /// <summary>要素属性</summary>
            private string m_csAttribute = null;
            /// <summary>要素単位</summary>
            private string m_csUnit = null;

            /// <summary><para>method outline:</para>
            /// <para>デフォルトコンストラクタ</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> 
            /// HySGeoRiverCrossSectionDataFileHeaderElement csHeaderElement = 
            ///         new HySGeoRiverCrossSectionDataFileHeaderElement(csName, csAttribute, csUnit) 
            /// </para>
            /// </example>
            /// <param name="csName">名称</param>
            /// <param name="csAttribute">属性</param>
            /// <param name="csUnit">単位</param>
            /// <returns>HySGeoRiverCrossSectionDataFileHeaderElement 生成したインスタンス</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public HySGeoRiverCrossSectionDataFileHeaderElement(string csName, string csAttribute, string csUnit)
            {
                m_csName = csName;
                m_csAttribute = csAttribute;
                m_csUnit = csUnit;
            }

            /// <summary><para>method outline:</para>
            /// <para>要素名称取得</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> string csName = GetName() </para>
            /// </example>
            /// <param name="">無し</param>
            /// <returns>要素名称</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public string GetName()
            {
                return m_csName;
            }
            /// <summary><para>method outline:</para>
            /// <para>要素名称設定</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> SetName( csName ) </para>
            /// </example>
            /// <param name="csName">要素名称</param>
            /// <returns>無し</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public void SetName(string csName)
            {
                m_csName = csName;
            }
            /// <summary><para>method outline:</para>
            /// <para>要素属性取得</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> string csAttribute = GetAttribute() </para>
            /// </example>
            /// <param name="">無し</param>
            /// <returns>要素属性</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public string GetAttribute()
            {
                return m_csAttribute;
            }
            /// <summary><para>method outline:</para>
            /// <para>要素属性設定</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> SetAttribute( csAttribute ) </para>
            /// </example>
            /// <param name="csAttribute">要素属性</param>
            /// <returns>無し</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public void SetAttribute(string csAttribute)
            {
                m_csAttribute = csAttribute;
            }
            /// <summary><para>method outline:</para>
            /// <para>要素単位取得</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> string csUnit = GetUnit() </para>
            /// </example>
            /// <param name="">河川無し</param>
            /// <returns>要素単位</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public string GetUnit()
            {
                return m_csUnit;
            }
            /// <summary><para>method outline:</para>
            /// <para>要素単位設定</para>
            /// </summary>
            /// <example><para>usage:</para>
            /// <para> SetUnit( csUnit ) </para>
            /// </example>
            /// <param name="csUnit">要素単位</param>
            /// <returns>無し</returns>
            /// <exception cref="">無し</exception>
            /// <remarks><para>remarks:</para>
            /// <para>無し</para>
            /// </remarks>
            public void SetUnit(string csUnit)
            {
                m_csUnit = csUnit;
            }
        }
    }
    
}
