// Copyright (C) 2010, 2013, 2014 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 504 2014-02-11 09:49:09Z panacoran $

using System;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace Protra.Lib.Config
{
    /// <summary>
    /// 設定ファイルを読み書きする抽象クラス。
    /// </summary>
    public abstract class ConfigBase
    {
        /// <summary>
        /// 設定ファイルの名前を取得する。
        /// </summary>
        protected abstract string ConfigName { get; }

        private readonly string _filename;
        private readonly XmlSerializer _serializer;
        private readonly FileChangeWatcher _watcher;

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        protected ConfigBase()
        {
// ReSharper disable once DoNotCallOverridableMethodsInConstructor
            _filename = Path.Combine(Global.DirConf, ConfigName + ".xml");
            _serializer = new XmlSerializer(GetType());
            if (!Directory.Exists(Global.DirConf))
                Directory.CreateDirectory(Global.DirConf);
            _watcher = new FileChangeWatcher(_filename) {ProcessFile = Load};
        }

        /// <summary>
        /// 設定ファイルを読み込む。
        /// </summary>
        public virtual void Load()
        {
            try
            {
                using (var reader = File.OpenText(_filename))
                {
                    var tmp = _serializer.Deserialize(reader);
                    foreach (var property in GetType().GetProperties())
                        if (property.GetCustomAttributes(typeof (XmlIgnoreAttribute), false).GetLength(0) == 0)
                            property.SetValue(this, property.GetValue(tmp, null), null);
                    Upgrade();
                }
            }
            catch (FileNotFoundException)
            {
            }
            catch (InvalidOperationException e)
            {
                using (new CenteredDialogHelper())
                    MessageBox.Show("設定ファイルが壊れています。\n" + _filename + "\n" + e.Message, "設定ファイルの読み込み",
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            // シリアライズで生成したオブジェクトのwatcherが有効にならないように、
            // コンストラクタの外で有効にする。
            _watcher.Enabled = true;
        }

        /// <summary>
        /// 廃止予定のプロパティの値を新しいプロパティに移す。
        /// </summary>
        protected virtual void Upgrade()
        {
        }

        /// <summary>
        /// 設定ファイルを書き込む。
        /// </summary>
        public virtual void Save()
        {
            try
            {
                if (_watcher != null)
                    _watcher.Enabled = false;
                Downgrade();
                using (var file = File.CreateText(_filename))
                    _serializer.Serialize(file, this);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                if (_watcher != null)
                    _watcher.Enabled = true;
            }
        }

        /// <summary>
        /// 新しいプロパティの値を廃止予定のプロパティに戻す。
        /// </summary>
        protected virtual void Downgrade()
        {
        }
    }
}