// Copyright (C) 2010 panacoran <panacoran@users.sourceforge.jp>
// 
// This program is part of Protra.
//
// Protra is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
// 
// $Id: ConfigBase.cs 301 2010-03-18 08:03:41Z panacoran $

using System;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using Protra.Lib.Dialogs;

namespace Protra.Lib.Config
{
    /// <summary>
    /// 設定ファイルを読み書きする抽象クラス。
    /// </summary>
    abstract public class ConfigBase
    {
        /// <summary>
        /// 古い設定ファイルを読み込む。
        /// </summary>
        abstract protected void ReadOldConfig();

        /// <summary>
        /// 設定ファイルの名前を取得する。
        /// </summary>
        abstract protected string ConfigName
        {
            get;
        }

        private string filename;
        private XmlSerializer serializer;
        FileSystemWatcher watcher;

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        /// <param name="watch">ファイルの変更を監視して再読み込みするかを指定。</param>
        public ConfigBase(bool watch)
        {
            filename = Path.Combine(Global.DirConf, ConfigName + ".xml");
            serializer = new XmlSerializer(GetType());
            if (!Directory.Exists(Global.DirConf))
                Directory.CreateDirectory(Global.DirConf);
            if (watch)
            {
                watcher = new FileSystemWatcher(Global.DirConf);
                watcher.Changed += new FileSystemEventHandler(watcher_Changed);
                watcher.Filter = Path.Combine(Global.DirConf, ConfigName + ".xml");
                watcher.EnableRaisingEvents = true;
            }
        }

        void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            Load();
        }

        /// <summary>
        /// 設定ファイルを読み込む。
        /// </summary>
        public virtual void Load()
        {
            try
            {
                using (StreamReader reader = new StreamReader(filename))
                {
                    Object tmp = serializer.Deserialize(reader);
                    foreach (PropertyInfo property in this.GetType().GetProperties())
                        property.SetValue(this, property.GetValue(tmp, null), null);
                }
            }
            catch (FileNotFoundException)
            {
                ReadOldConfig();
                Save();
            }
            catch
            {
                using (ApplicationError dialog = new ApplicationError())
                {
                    dialog.ErrorMessage = "設定ファイルが壊れています。\n" + filename;
                    dialog.Mode = ApplicationError.ErrorType.Recoverable;
                    dialog.ShowDialog();
                }
            }
        }

        /// <summary>
        /// 設定ファイルを書き込む。
        /// </summary>
        public virtual void Save()
        {
            try
            {
                if (watcher != null)
                    watcher.EnableRaisingEvents = false;
                using (StreamWriter writer = new StreamWriter(filename))
                    serializer.Serialize(writer, this);
            }
            catch
            {
                using (ApplicationError dialog = new ApplicationError())
                {
                    dialog.ErrorMessage = "設定ファイルの書き込みに失敗しました。\n" + filename;
                    dialog.Mode = ApplicationError.ErrorType.Recoverable;
                    dialog.ShowDialog();
                }
            }
            finally
            {
                if (watcher != null)
                    watcher.EnableRaisingEvents = true;
            }
        }
    }
}
