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
    {
        static string LogPath(string system, int mode)
        {
            if (!Directory.Exists(Global.DirLog))
                Directory.CreateDirectory(Global.DirLog);
            return Path.Combine(Global.DirLog,
                HttpUtility.UrlEncode(system.Substring(0, system.Length - 3)) + "&" + mode);
        }

        /// <summary>
        /// 指定された実行履歴を取得する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="mode">実行モード</param>
        /// <param name="code">証券コード</param>
        /// <returns>日付順にソートされた実行履歴</returns>
        static public List<Log> GetLog(string system, int mode, string code)
        {
            var logs = new List<Log>();
            var f = LogPath(system, mode);
            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="mode">実行モード</param>
        /// <param name="min">日付の最小値</param>
        /// <param name="max">日付の最大値</param>
        /// <returns>日付の降順にソートされたログ</returns>
        static public List<Log> GetLog(string system, int mode, DateTime min, DateTime max)
        {
            var logs = new List<Log>();
            var f = LogPath(system, mode);
            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="mode">実行モード</param>
        /// <param name="log">追加する実行履歴</param>
        static public void Add(string system, int mode, Log log)
        {
            var f = LogPath(system, mode);
            using (var b = new BinaryWriter(new FileStream(f, FileMode.Append)))
                log.Write(b);
        }
 
        /// <summary>
        /// 指定された実行履歴を削除する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="mode">実行モード</param>
        static public void Delete(string system, int mode)
        {
            try
            {
                File.Delete(LogPath(system, mode));
            }
            catch (DirectoryNotFoundException)
            {}
        }

        /// <summary>
        /// 指定された実行履歴を削除する。
        /// </summary>
        /// <param name="system">システムのファイル名</param>
        /// <param name="mode">実行モード</param>
        /// <param name="code">証券コード</param>
        static public void Delete(string system, int mode, string code)
        {
            List<Log> logs = new List<Log>();
            var f = LogPath(system, mode);
            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>
        static public void DeleteAll()
        {
            try
            {
                Directory.Delete(Global.DirLog, true);
            }
            catch (DirectoryNotFoundException)
            {}
        }
    }
}
