// Copyright (C) 2013 panacoran <panacoran@users.sourceforge.jp>
// Copyright (C) 2003 Daisuke Arai <darai@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$

using System;
using System.Collections;
using System.Collections.Generic;

namespace PtSim
{
    /// <summary>
    /// PricePairの日付順のリスト
    /// </summary>
    public class PricePairList : IEnumerable<PricePair>
    {
        private readonly SortedList<DateTime, PricePair> _sortedList =
            new SortedList<DateTime, PricePair>();

        /// <summary>
        /// リストの要素数を取得する。
        /// </summary>
        public int Count
        {
            get { return _sortedList.Count; }
        }

        /// <summary>
        /// 指定された日付のPricePairを取得または設定する。
        /// </summary>
        /// <param name="date">日付</param>
        /// <returns></returns>
        public PricePair this[DateTime date]
        {
            get
            {
                if (!_sortedList.ContainsKey(date))
                    _sortedList[date] = new PricePair();
                return _sortedList[date];
            }
            set { _sortedList[date] = value; }
        }

        /// <summary>
        /// 指定されたインデックスのPricePairを取得する。
        /// </summary>
        /// <param name="i">インデックス</param>
        /// <returns></returns>
        public PricePair this[int i]
        {
            get { return _sortedList.Values[i]; }
        }

        /// <summary>
        /// 日付のリストを取得する。
        /// </summary>
        public IList<DateTime> Dates
        {
            get { return _sortedList.Keys; }
        }

        /// <summary>
        /// 値の積算値のリストを取得する。
        /// </summary>
        public PricePairList AccumulatedList
        {
            get
            {
                var list = new PricePairList();
                var sum = new PricePair();
                foreach (var date in _sortedList.Keys)
                {
                    sum += _sortedList[date];
                    list[date] = sum;
                }
                return list;
            }
        }

        /// <summary>
        /// 簿価のみ積算したリストを取得する。
        /// </summary>
        public PricePairList BookAccumulatedList
        {
            get
            {
                var list = new PricePairList();
                var sum = 0f;
                foreach (var date in _sortedList.Keys)
                {
                    var pair = _sortedList[date];
                    sum += pair.Book;
                    list[date] = new PricePair(pair.Market, sum);
                }
                return list;
            }
        }

        /// <summary>
        /// PricePairListを反復処理する列挙子を返す。
        /// </summary>
        /// <returns>列挙子</returns>
        public IEnumerator<PricePair> GetEnumerator()
        {
            return _sortedList.Values.GetEnumerator();
        }

        /// <summary>
        /// PricePairListを反復処理する列挙子を返す。
        /// </summary>
        /// <returns>列挙子</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return _sortedList.Values.GetEnumerator();
        }
    }

    /// <summary>
    /// 時価と簿価の両方について計算するクラス
    /// </summary>
    public class PricePair
    {
        /// <summary>
        /// 時価を取得または設定する。
        /// </summary>
        public float Market { get; private set; }

        /// <summary>
        /// 簿価を取得または設定する。
        /// </summary>
        public float Book { get; private set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public PricePair()
        {
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="market">時価</param>
        /// <param name="book">簿価</param>
        public PricePair(float market, float book)
        {
            Market = market;
            Book = book;
        }

        /// <summary>
        /// 時価を加算する。
        /// </summary>
        /// <param name="value">時価</param>
        public void AddMarket(float value)
        {
            Market += value;
        }

        /// <summary>
        /// 時価を加算する。
        /// </summary>
        /// <param name="quantity">数量</param>
        /// <param name="price">価格</param>
        public void AddMarket(float quantity, float price)
        {
            Market += quantity * price;
        }

        /// <summary>
        /// 簿価を加算する。
        /// </summary>
        /// <param name="value">時価</param>
        public void AddBook(float value)
        {
            Book += value;
        }

        /// <summary>
        /// 簿価を加算する。
        /// </summary>
        /// <param name="quantity">数量</param>
        /// <param name="price">価格</param>
        public void AddBook(float quantity, float price)
        {
            Book += quantity * price;
        }

        /// <summary>
        /// 二つの値を加算する。
        /// </summary>
        /// <param name="v1">最初の値</param>
        /// <param name="v2">二つ目の値</param>
        /// <returns></returns>
        public static PricePair operator +(PricePair v1, PricePair v2)
        {
            return new PricePair(v1.Market + v2.Market, v1.Book + v2.Book);
        }

        /// <summary>
        /// 最初の値から二つ目の値を減算する。
        /// </summary>
        /// <param name="v1">最初の値</param>
        /// <param name="v2">二つ目の値</param>
        /// <returns></returns>
        public static PricePair operator -(PricePair v1, PricePair v2)
        {
            return new PricePair(v1.Market - v2.Market, v1.Book - v2.Book);
        }

        /// <summary>
        /// 二つの値の時価と簿価それぞれについて大きいほうを格納した値を返す。
        /// </summary>
        /// <param name="v1">最初の値</param>
        /// <param name="v2">二つ目の値</param>
        /// <returns></returns>
        public static PricePair Max(PricePair v1, PricePair v2)
        {
            return new PricePair(Math.Max(v1.Market, v2.Market),
                                 Math.Max(v1.Book, v2.Book));
        }

        /// <summary>
        /// 二つの値の時価と簿価それぞれについて小さいほうを格納した値を返す。
        /// </summary>
        /// <param name="v1">最初の値</param>
        /// <param name="v2">二つ目の値</param>
        /// <returns></returns>
        public static PricePair Min(PricePair v1, PricePair v2)
        {
            return new PricePair(Math.Min(v1.Market, v2.Market),
                                 Math.Min(v1.Book, v2.Book));
        }
    }
}