﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    /// <summary>
    /// キーレスINIファイルを読み書きするクラス
    /// </summary>
    public class KeylessIniFile
    {
        //INIファイルのパス
        private string filePath = "";
        //INIファイルの中身格納
        List<string> lines = new List<string>();

        /// <summary>
        /// ファイル名を指定して初期化します。
        /// ファイルが存在しない場合は初回書き込み時に作成されます。
        /// </summary>
        public KeylessIniFile(string _filePath)
        {
            this.filePath = @_filePath;

            if (File.Exists(@_filePath))
            {
                using (var reader = new StreamReader(@_filePath, Encoding.GetEncoding("shift-jis")))
                {
                    //ファイルの最終行まで繰り返し処理
                    while (!reader.EndOfStream)
                    {
                        //1行取得する
                        var line = reader.ReadLine();
                        lines.Add(line);
                    }
                }
            }
        }

        /// <summary>
        /// セクションのみ指定の場合、valueのリストを返す。
        /// </summary>
        /// <param name="section">セクション名</param>
        /// <returns>該当セクションがない場合は空のリストを返す。</returns>
        public List<string> this[string section]
        {
            get
            {
                //var list = new List<string>();

                ////該当セクションがない時は空文字が返る
                //var res = GetPrivateProfileString(section, string.Empty, string.Empty);

                //if (res != string.Empty)
                //{
                //    string[] del = { "\r\n" };
                //    string[] arr = res.Split(del, StringSplitOptions.None);

                //    foreach (string data in arr)
                //    {
                //        //☆空行とコメント行は除外
                //        //※コメント行にも空文字は含まれている！
                //        if (data != String.Empty && !data.StartsWith(";"))
                //            list.Add(data);
                //    }
                //}

                return GetPrivateProfileSection(section);

                //return list;
            }
        }

        /// <summary>
        /// sectionとkeyからiniファイルの設定値を取得、設定します。
        /// </summary>
        /// <returns>指定したsectionとkeyの組合せが無い場合は""が返ります。</returns>
        public string this[string section, string key]
        {
            set
            {
                /*
                 * プロパティを使うと値の取得・変更を「set{}，get{}」だけでひとかたまりに記述する事ができます。
                 * また、利用者側のクラスでは、変数に直接アクセスしているかのように、
                 * 「インスタンス.プロパティ名」だけで値のやりとりができるようになるのがプロパティの利点です。    
                 * 
                 * 書き方としてまずプロパティを記述するクラスでは「アクセスレベル型　プロパティ名{}」で宣言し、
                 * その中に「set{}，get{}」を記述します。
                 * 
                 * set{}の中には主に「メンバ変数　=　value」というように記述します。
                 * 「value」は利用者側のクラスで代入された値が格納される予約語です。
                 * 
                 * get{}の中には「return　メンバ変数」というように、戻り値を指定する形になります。
                 * その後利用者側のクラスでインスタンスを生成し、値の取得時は「インスタンス名.プロパティ名」で get{} を呼び出し、
                 * 「インスタンス名.プロパティ名　=　値」で set{} を呼び出します。
                 */
                WritePrivateProfileString(section, key, value, filePath);
            }
            get
            {
                return GetPrivateProfileString(section, key, string.Empty);
            }
        }

        /// <summary>
        /// sectionとkeyからiniファイルの設定値を取得します。
        /// 指定したsectionとkeyの組合せが無い場合はdefaultvalueで指定した値が返ります。
        /// </summary>
        /// <returns>指定したsectionとkeyの組合せが無い場合はdefaultvalueで指定した値が返ります。</returns>
        public string this[string section, string key, string defaultvalue]
        {
            get
            {
                return GetPrivateProfileString(section, key, defaultvalue);
            }
        }

        //ここにファイルからの読み込みの実装を書く

        //●セクションのデータ(Key, Value)をリストにして返す
        List<string> GetPrivateProfileSection(string section)
        {
            List<string> data = new List<string>();
            bool SectionFound = false;
            Match matche;

            foreach (string line in lines)
            {
                //セクション検索
                if (SectionFound == false)
                {
                    //※大文字小文字同一視
                    matche = Regex.Match(line, "^\\[" + section + "\\]$", RegexOptions.IgnoreCase);
                    if (matche.Success)
                    {
                        SectionFound = true;
                        continue;
                    }
                }
                else
                {
                    //セクション内のデータをリストに入れる
                    //※別セクションに入った時は抜ける
                    matche = Regex.Match(line, "^\\[.+\\]$", RegexOptions.IgnoreCase);
                    if (matche.Success)
                    {
                        SectionFound = false;
                        continue;
                    }

                    //☆空行とコメント行は除外
                    //※コメント行にも空文字は含まれている！
                    if (line != String.Empty && !line.StartsWith(";"))
                    {
                        data.Add(line);
                    }
                }
            }

            return data;
        }

        /// <returns>指定したsectionとkeyの組合せが無い場合はdefaultvalueで指定した値が返ります。</returns>
        #region INIファイルからの読み込み実装
        private string GetPrivateProfileString(string _section, string _key, string _defaultvalue)
        {
            string text = "";

            //using (StreamReader sr = new StreamReader(_filePath, Encoding.GetEncoding("Shift_JIS")))
            //{
            //    text = sr.ReadToEnd();
            //}


            //大文字小文字同一視
            Match matche1 = Regex.Match(text, "\\[" + _section + "\\]" + "[^[]*", RegexOptions.IgnoreCase);
            //[^\[^\]]*
            if (!matche1.Success)
            {
                //該当セクションがなければここで終了
                return _defaultvalue;
            }
            var text2 = matche1.Value;

            //キー指定がない場合
            if (_key == String.Empty)
            {
                //セクション名を除いたvaluesを返す
                var str1 = text2.Replace("[" + _section + "]", "");
                var str2 = str1.Trim();
                return str2;
            }

            var matche2 = Regex.Match(text2, _key + "=.*\\r\\n");
            if (!matche2.Success)
            {
                //該当キーがなければここで終了
                return _defaultvalue;
            }
            var text3 = matche2.Value;

            var result = text3.Trim().Replace(_key + "=", "");
            return result;
        }
        #endregion

        ///イニシャルファイル書き込み
        private void WritePrivateProfileString(string section, string key, string val, string filePath)
        {
            if (!File.Exists(filePath))
            {
                //ファイルがない
                return;
            }

            var lines = new List<string>();
            string line = "";
            string newKeyValue = key + "=" + val;

            try
            {
                using (StreamReader sr = new StreamReader(filePath, Encoding.GetEncoding("Shift_JIS")))
                {
                    while ((line = sr.ReadLine()) != null)
                    {
                        lines.Add(line);
                    }
                }
            }
            catch (FileNotFoundException ex)
            {
                MessageBox.Show(ex.Message);
            }

            int indexSect = lines.IndexOf("[" + section + "]");
            int indexKey = 0;
            foreach (string str in lines)
            {
                if (str.StartsWith(key + "=")) break;
                indexKey++;
            }

            //該当セクション・該当キーがないなら書き込まない
            //※手抜き実装
            if (indexKey <= indexSect)
            {
                return;
            }
            //値に変更ないなら書き込まない
            if (lines[indexKey] == newKeyValue) { return; }

            //値更新
            lines[indexKey] = newKeyValue;

            //上書きで書き込む
            using (StreamWriter file = new StreamWriter(filePath, false, Encoding.GetEncoding("Shift_JIS")))
            {
                foreach (string str in lines)
                {
                    file.WriteLine(str);
                }
            }
        }
    }
}