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

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using Protra.Lib.Db;

namespace Protra.Lib.Update
{
	/// <summary>
	/// 株価データダウンロードサイトを利用して株価データの更新を行うクラス。
	/// </summary>
	public class KdbComUpdator: PriceDataUpdator
	{
        /// <summary>
        /// データが存在する最初の日付を取得する。
        /// </summary>
        public override DateTime DataSince
        {
            get {
                DateTime tmp = DateTime.Now.AddMonths(-6);
                return new DateTime(tmp.Year, tmp.Month, tmp.Day);
            }
        }

        /// <summary>
        /// 新しいデータが置かれる時刻に達しているか。
        /// </summary>
        /// <param name="time">時刻</param>
        /// <returns></returns>
        protected override bool IsDataAvailable(DateTime time)
        {
            return time.Hour >= 16;
        }

        /// <summary>
        /// データのURLを取得する。
        /// </summary>
        /// <returns>URL</returns>
        protected override string DownloadUrl()
        {
            return "http://k-db.com/site/download.aspx?date=" + Date.ToString(@"yyyy-MM-dd") + "&p=stock&q=d";
        }

        private Dictionary<DateTime, double> indexVolume = new Dictionary<DateTime, double>();

        /// <summary>
		/// 株価データを更新する。
		/// </summary>
		/// <param name="worker">BackgroundWorker</param>
        /// <param name="e">DoWorkイベントの引数</param>
		protected override void UpdatePrice(BackgroundWorker worker, DoWorkEventArgs e)
		{
            GetIndexVolume();
            base.UpdatePrice(worker, e);
        }

        void GetIndexVolume()
        {
            var dl = new DownloadUtil();
            dl.Url = "http://k-db.com/site/toukei.aspx?market=T1&download=csv";
            Stream stream = dl.GetResponse();
            if (stream == null)
                throw new ApplicationException("市場統計データの読み込みに失敗しました。");
            using (var reader = new StreamReader(stream, Encoding.GetEncoding("shift_jis")))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    string[] tokens = line.Split(',');
                    switch (tokens[0])
                    {
                        case "東証1部":
                        case "日付":
                            continue;
                    }
                    var date = DateTime.Parse(tokens[0]);
                    var volume = Math.Round(double.Parse(tokens[1]) / 1000); // 無尽蔵に合わせるため丸める。
                    indexVolume[date] = volume;
                }
            }
        }

        /// <summary>
        /// 指数のデータを読んで価格データを返す。
        /// </summary>
        /// <param name="prices">価格データのリスト</param>
        protected override void ReadIndexData(List<PriceData> prices)
        {
            var dl = new DownloadUtil();
            dl.Url = "http://k-db.com/site/download.aspx?date=" + Date.ToString(@"yyyy-MM-dd") + "&p=index&q=d";
            Stream stream = dl.GetResponse();
            if (stream == null)
                throw new ApplicationException("指数データの読み込みに失敗しました。");
            using (var reader = new StreamReader(stream, Encoding.GetEncoding("shift_jis")))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    string[] tokens = line.Split(',');
                    if (tokens[0] != "日経平均" && tokens[0] != "TOPIX")
                        continue;
                    var r = new PriceData();
                    r.Code = tokens[0] == "日経平均" ? 1001 : 1002;
                    r.Date = Date;
                    r.Name = tokens[0];
                    r.MarketId = MarketId.Tokyo1;
                    r.Open = (int)ParseField(tokens[1]);
                    r.High = (int)ParseField(tokens[2]);
                    r.Low = (int)ParseField(tokens[3]);
                    r.Close = (int)ParseField(tokens[4]);
                    r.Volume = indexVolume[Date];
                    prices.Add(r);
                }
            }
        }

        /// <summary>
        /// 文字列に含まれるデータを格納したオブジェクトを返す。
        /// </summary>
        /// <param name="line">文字列を指定する。</param>
        /// <returns>オブジェクト</returns>
        protected override PriceData ParseLine(string line)
        {
            string[] tokens = line.Split(',');
            var r = new PriceData();
            try
            {
			    r.Code = int.Parse(tokens[0]);
				switch (tokens[1])
				{
					case "東証":
					case "東証1部":
						r.MarketId = MarketId.Tokyo1;
						break;
					case "東証2部":
						r.MarketId = MarketId.Tokyo2;
						break;
					case "東証マザーズ":
                    case "東証マザーズ外国部":
						r.MarketId = MarketId.Mothers;
						break;
                    case "東証1部外国部":
                        r.MarketId = MarketId.TokyoForeign;
                        break;
                    case "大証":
                    case "大証1部":
                        r.MarketId = MarketId.Osaka1;
                        break;
                    case "大証2部":
                        r.MarketId = MarketId.Osaka2;
                        break;
                    case "ＨＣスタンダード":
                    case "ＨＣスタンダード外国部":
                    case "ＨＣグロース":
                        r.MarketId = MarketId.Hercules;
                        break;
                    case "ＪＱ":
                    case "ＪＱNEO":
                    case "ＪＱスタンダード":
                    case "ＪＱスタンダード外国部":
                    case "ＪＱグロース":
                        r.MarketId = MarketId.Jasdaq;
                        break;
					default:
						return null;
                }
                r.Date = Date;
				r.Name = tokens[2];
                r.Open = (int)ParseField(tokens[4]);
                r.High = (int)ParseField(tokens[5]);
                r.Low = (int)ParseField(tokens[6]);
                r.Close = (int)ParseField(tokens[7]);
                r.Volume = ParseField(tokens[8]) / 1000;
            }
            catch (FormatException)
            {
                // フォーマットエラーは全部無視する。
                return null;
            }
            return r;
        }

		private double ParseField(string s)
		{
			// 無効なフィールドを示す"-"を0として扱う。
			if (s == "-")
				return 0;
			// 指数の値に小数が含まれているのでdouble.Parseを利用する。
			return double.Parse(s);
		}
	}
}
