// Copyright(C) 2010 panacorn <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: BrandData.cs 414 2011-03-22 14:11:23Z panacoran $

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Protra.Lib.Dialogs;
using Protra.Lib.Update;

namespace Protra.Lib.Data
{
    /// <summary>
    /// 分割情報を格納するクラス。
    /// </summary>
    public class Split
    {
        /// <summary>
        /// 基準日。
        /// </summary>
        public DateTime Date { set; get; }
        /// <summary>
        /// 分割比率。
        /// </summary>
        public double Ratio { set; get; }
    }

    /// <summary>
    /// 銘柄データを格納するクラス。
    /// </summary>
    public class Brand
    {
        /// <summary>
        /// 銘柄のフラグ
        /// </summary>
        public enum Flag
        {
            /// <summary>
            /// 上場廃止。
            /// </summary>
            OBS = 1,
            /// <summary>
            /// 日経平均採用銘柄。
            /// </summary>
            N255 = 2,
            /// <summary>
            /// 売買代金上位500位。
            /// </summary>
            A500 = 4
        }
        /// <summary>
        /// コードを取得または設定する。
        /// </summary>
        public string Code { get; set; }
        /// <summary>
        /// 銘柄名を取得または設定する。
        /// </summary>
        public string Name { get;  set; }
        /// <summary>
        /// 市場を取得または設定する。
        /// </summary>
        public string Market { get; set; }
        /// <summary>
        /// 単元を取得または設定する。
        /// </summary>
        public int Unit { get; set; }
        /// <summary>
        /// フラグを取得または設定する。
        /// </summary>
        public Flag Flags { set; get; }
        /// <summary>
        /// 分割情報を取得または設定する。
        /// </summary>
        public List<Split> Split { set; get; }

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        public Brand()
        {
            Split = new List<Split>();
        }
    }

    /// <summary>
    /// 銘柄データを管理するクラス。
    /// </summary>
    public class BrandData
    {
        Dictionary<string, Brand> data;

        /// <summary>
        /// 最終更新日時を取得または設定する。
        /// </summary>
        public DateTime LastModified { private set; get; }

        /// <summary>
        /// コードに対応する銘柄情報を取得する。
        /// </summary>
        /// <param name="code">コード。</param>
        /// <returns>銘柄情報。</returns>
        public Brand this[string code]
        {
            get
            {
                if (code.Length > 4)
                    return data[code.Substring(0, 4)];
                return data[code];
            }
        }

        /// <summary>
        /// 銘柄情報を読み込む。
        /// </summary>
        public void Load()
        {
            data = new Dictionary<string, Brand>();
            var b = new Brand();
            b.Code = "1001";
            b.Market = "T";
            b.Name = "日経平均";
            b.Unit = 1;
            data[b.Code] = b;
            b = new Brand();
            b.Code = "1002";
            b.Market = "T";
            b.Name = "ＴＯＰＩＸ";
            b.Unit = 1;
            data[b.Code] = b;
            try
            {
                using (var reader = new StreamReader(OpenIndex(), Encoding.GetEncoding("shift_jis")))
                {
                    reader.ReadLine(); // @dateを読み飛ばす。
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        var brand = new Brand();
                        string[] entries = line.Split(',');
                        brand.Code = entries[0];
                        brand.Name = entries[1];
                        brand.Market = entries[2];
                        brand.Unit = int.Parse(entries[3]);
                        for (int i = 4; i < entries.Length; i++)
                        {
                            if (entries[i] == "OBS")
                                brand.Flags |= Brand.Flag.OBS;
                            else if (entries[i] == "N225")
                                brand.Flags |= Brand.Flag.N255;
                            else if (entries[i] == "A500")
                                brand.Flags |= Brand.Flag.A500;
                            else if (!entries[i].StartsWith("S:"))
                                throw new ApplicationException("index.txtが不正です。:\n" + line);
                            else
                            {
                                // 分割比率を処理。
                                var split = new Split();
                                int y = int.Parse(entries[i].Substring(2, 4));
                                int m = int.Parse(entries[i].Substring(6, 2));
                                int d = int.Parse(entries[i].Substring(8, 2));
                                split.Date = new DateTime(y, m, d);
                                split.Ratio = double.Parse(entries[i].Substring(11));
                                brand.Split.Add(split);
                            }
                        }
                        data.Add(brand.Code, brand);
                    }
                }
            }
            catch (Exception e)
            {
                using (var dialog = new ApplicationError())
                {
                    dialog.ErrorMessage = e.Message;
                    dialog.Mode = ApplicationError.ErrorType.Fatal;
                    dialog.ShowDialog();
                    Environment.Exit(1);
                }
            }
        }

        private Stream OpenIndex()
        {
            var file = Path.Combine(Global.DirData, "index.txt");
            try
            {
                Stream stream = File.Open(file, FileMode.Open);
                LastModified = File.GetLastWriteTime(file);
                return stream;
            }
            catch (FileNotFoundException)
            {
                var tmp = Path.Combine(Global.DirTmp, "index.txt");
                if (File.Exists(tmp))
                {
                    File.Copy(tmp, file);
                    return OpenIndex();
                }
                var dl = new DownloadUtil();
                dl.Url = "http://protra.sourceforge.jp/data/index.txt.lzh";
                Stream stream = dl.DownloadAndExtract();
                if (stream == null)
                    throw new ApplicationException("index.txtのダウンロードに失敗しました。");
                stream.Close();
                return OpenIndex();
            }
        }
    }
}
