// Copyright(C) 2010, 2013 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: LogData.cs 450 2013-05-25 14:32:09Z panacoran $

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;

namespace Protra.Lib.Data
{
    /// <summary>
    /// 注文の種類を表す列挙型。
    /// </summary>
    public enum Order
    {
        /// <summary>
        /// 買い。
        /// </summary>
        Buy,

        /// <summary>
        /// 売り。
        /// </summary>
        Sell
    }

    /// <summary>
    /// システムの実行履歴を表すクラス。
    /// </summary>
    public class Log : IComparable<Log>
    {
        /// <summary>
        /// コードを取得または設定する。
        /// </summary>
        public string Code { get; set; }

        /// <summary>
        /// 日付を取得または設定する。
        /// </summary>
        public DateTime Date { get; set; }

        /// <summary>
        /// 価格を取得または設定する。
        /// </summary>
        public int Price { get; set; }

        /// <summary>
        /// 数量を取得または設定する。
        /// </summary>
        public int Quantity { get; set; }

        /// <summary>
        /// 注文の種類を取得または設定する。
        /// </summary>
        public Order Order { get; set; }

        /// <summary>
        /// 指定されたオブジェクトと自分を比較する。
        /// </summary>
        /// <param name="other">比較対象のオブジェクト</param>
        /// <returns>比較結果</returns>
        public int CompareTo(Log other)
        {
            return other.Date.CompareTo(Date);
        }

        /// <summary>
        /// ファイルから読み込む。
        /// </summary>
        /// <param name="b">ファイルのBinaryReader</param>
        /// <returns></returns>
        public Log Read(BinaryReader b)
        {
            Code = b.ReadString();
            Date = new DateTime((long)b.ReadInt32() * 86400 * 10000000);
            Price = b.ReadInt32();
            Quantity = b.ReadInt32();
            Order = (Order)b.ReadByte();
            return this;
        }

        /// <summary>
        /// ファイルに書き込む。
        /// </summary>
        /// <param name="b">ファイルのBinaryWriter</param>
        public void Write(BinaryWriter b)
        {
            b.Write(Code);
            b.Write((int)(Date.Ticks / 86400 / 10000000));
            b.Write(Price);
            b.Write(Quantity);
            b.Write((byte)Order);
        }
    }

    /// <summary>
    /// システムの実行履歴を操作するクラス。
    /// </summary>
    public class LogData
    {
        private static string LogPath(string system, TimeFrame priceType)
        {
            if (!Directory.Exists(Global.DirLog))
                Directory.CreateDirectory(Global.DirLog);
            return Path.Combine(Global.DirLog,
                                HttpUtility.UrlEncode(system.Substring(0, system.Length - 3)) + "&" + (int)priceType);
        }

        /// <summary>
        /// 指定された実行履歴を取得する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="priceType">株価データの種類</param>
        /// <param name="code">証券コード</param>
        /// <returns>日付順にソートされた実行履歴</returns>
        public static List<Log> GetLog(string system, TimeFrame priceType, string code)
        {
            var logs = new List<Log>();
            var f = LogPath(system, priceType);
            if (!File.Exists(f))
                return logs;
            using (var b = new BinaryReader(new FileStream(f, FileMode.Open)))
                try
                {
                    while (true)
                    {
                        var log = new Log().Read(b);
                        if (log.Code == code)
                            logs.Add(log);
                    }
                }
                catch (EndOfStreamException)
                {
                }
            return logs;
        }

        /// <summary>
        /// 指定された実行履歴を取得する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="priceType">株価データの種類</param>
        /// <param name="min">日付の最小値</param>
        /// <param name="max">日付の最大値</param>
        /// <returns>日付の降順にソートされたログ</returns>
        public static List<Log> GetLog(string system, TimeFrame priceType, DateTime min, DateTime max)
        {
            var logs = new List<Log>();
            var f = LogPath(system, priceType);
            if (!File.Exists(f))
                return logs;
            using (var b = new BinaryReader(new FileStream(f, FileMode.Open)))
                try
                {
                    while (true)
                    {
                        var log = new Log().Read(b);
                        if (log.Date >= min && log.Date <= max)
                            logs.Add(log);
                    }
                }
                catch (EndOfStreamException)
                {
                }
            logs.Sort();
            return logs;
        }

        /// <summary>
        /// 実行履歴を追加する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="priceType">株価データの種類</param>
        /// <param name="log">追加する実行履歴</param>
        public static void Add(string system, TimeFrame priceType, Log log)
        {
            var f = LogPath(system, priceType);
            using (var b = new BinaryWriter(new FileStream(f, FileMode.Append)))
                log.Write(b);
        }

        /// <summary>
        /// 指定された実行履歴を削除する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="priceType">株価データの種類</param>
        public static void Delete(string system, TimeFrame priceType)
        {
            try
            {
                File.Delete(LogPath(system, priceType));
            }
            catch (DirectoryNotFoundException)
            {
            }
        }

        /// <summary>
        /// 指定された実行履歴を削除する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="priceType">株価データの種類</param>
        /// <param name="code">証券コード</param>
        public static void Delete(string system, TimeFrame priceType, string code)
        {
            var logs = new List<Log>();
            var f = LogPath(system, priceType);
            try
            {
                if (!File.Exists(f))
                    return;
            }
            catch (DirectoryNotFoundException)
            {
                return;
            }
            using (var r = new BinaryReader(new FileStream(f, FileMode.Open)))
                try
                {
                    while (true)
                    {
                        var log = new Log();
                        log.Read(r);
                        if (log.Code != code)
                            logs.Add(log);
                    }
                }
                catch (EndOfStreamException)
                {
                }
            using (var w = new BinaryWriter(new FileStream(f, FileMode.Truncate)))
                foreach (var log in logs)
                    log.Write(w);
        }

        /// <summary>
        /// 実行履歴をすべて削除する。
        /// </summary>
        public static void DeleteAll()
        {
            try
            {
                Directory.Delete(Global.DirLog, true);
            }
            catch (DirectoryNotFoundException)
            {
            }
        }
    }
}