﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using TypedDataGrid.Utilities;

namespace TypedDataGrid.Settings
{
    public class ColumnSettings<TData> : IEnumerable<ColumnSetting<TData>>
    {
        private Dictionary<string, ColumnSetting<TData>> dict;


        public ColumnSettings()
        {
            this.dict = new Dictionary<string, ColumnSetting<TData>>();
        }

        public ColumnSettings(IEnumerable<ColumnSetting<TData>> list)
            : this()
        {
            foreach (var item in list)
            {
                this.Add(item);
            }
        }



        public void Add(ColumnSetting<TData> setting)
        {
            setting.Index = this.dict.Count;
            this.AddInternal(setting);
        }

        private void AddInternal(ColumnSetting<TData> setting)
        {
            if (string.IsNullOrEmpty(setting.Name))
            {
                throw new ArgumentException("Name が設定されていません。", "setting");
            }

            this.dict.Add(setting.Name.ToUpperInvariant(), setting);
        }

        public void Insert(int index, ColumnSetting<TData> setting)
        {
            if (index < 0)
            {
                index = 0;
            }
            else if (this.dict.Count <= index)
            {
                index = this.dict.Count;
            }

            foreach (var col in this.dict)
            {
                if (col.Value.Index >= index)
                {
                    col.Value.Index += 1;
                }
            }

            setting.Index = index;

            this.AddInternal(setting);
        }

        public void InsertBefore(Expression<Func<TData, object>> propertyAccessor, ColumnSetting<TData> setting)
        {
            var name = NameExtensions.NameOf(propertyAccessor);
            ColumnSetting<TData> beforeSetting;
            if (!this.TryGetSetting(name, out beforeSetting))
            {
                throw new KeyNotFoundException();
            }

            this.Insert(beforeSetting.Index, setting);
        }


        /// <summary>
        /// 指定された名前の<see cref="ColumnSetting{TData}"/>を取得します。
        /// </summary>
        /// <param name="name">取得する<see cref="ColumnSetting{TData}"/>の名前を指定します。</param>
        /// <param name="setting">取得した<see cref="ColumnSetting{TData}"/>が格納されます。</param>
        /// <returns>取得に成功したときはtrueが返ります。</returns>
        public bool TryGetSetting(string name, out ColumnSetting<TData> setting)
        {
            return this.dict.TryGetValue(name.ToUpperInvariant(), out setting);
        }

        /// <summary>
        /// 指定された名前の<see cref="ColumnSetting{TData}"/>を取得します。
        /// </summary>
        /// <param name="name">取得する<see cref="ColumnSetting{TData}"/>の名前を指定します。</param>
        /// <returns>取得した<see cref="ColumnSetting{TData}"/>が返ります。</returns>
        public ColumnSetting<TData> GetSetting(string name)
        {
            return this.dict[name.ToUpperInvariant()];
        }

        /// <summary>
        /// 指定された名前の<see cref="ColumnSetting{TData}"/>を取得します。
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="nameAccessor">取得する<see cref="ColumnSetting{TData}"/>の名前のアクセサーを指定します。</param>
        /// <returns>取得した<see cref="ColumnSetting{TData}"/>が返ります。</returns>
        public ColumnSetting<TData> GetSetting<TValue>(Expression<Func<TData, TValue>> nameAccessor)
        {
            return this.GetSetting(NameExtensions.NameOf(nameAccessor));
        }


        #region IEnumerable<ColumnSetting> メンバー

        public IEnumerator<ColumnSetting<TData>> GetEnumerator()
        {
            return this.dict.Values.GetEnumerator();
        }

        #endregion

        #region IEnumerable メンバー

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion
    }
}
