// 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: KabukaJohoUpdator.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 KabukaJohoUpdator: PriceDataUpdator
	{
        Dictionary<int, string> mnameTable;
        int mnameYear = 0;

        /// <summary>
        /// データが存在する最初の日付を取得する。
        /// </summary>
        public override DateTime DataSince
        {
            get { return new DateTime(2000, 1, 4); }
        }

        /// <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://www.edatalab.net/kabu/data" +
                    Date.ToString((Date.Year < 2006)
                        ? @"yyyy\/yyyyMMdd"
                        : @"yyyy\/\DyyMMdd") + ".LZH";
        }

        /// <summary>
        /// 銘柄名データを読む。
        /// </summary>
        protected override void ReadNameData()
        {
            if (mnameYear == Date.Year)
                return;
            mnameTable = new Dictionary<int, string>();
            var dl = new DownloadUtil();
            dl.Url = "http://www.edatalab.net/kabu/data" + Date.ToString("yyyy") + "/MNAME.LZH";
            Stream stream = dl.DownloadAndExtract();
            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(',');
                    try
                    {
                        mnameTable.Add(int.Parse(tokens[0]), tokens[1].Trim('"'));
                    }
                    catch (FormatException)
                    {
                        continue;
                    }
                }
            }
        }

        /// <summary>
        /// 文字列に含まれるデータを格納したオブジェクトを返す。
        /// </summary>
        /// <param name="line">文字列を指定する。</param>
        /// <returns>オブジェクト</returns>
        protected override PriceData ParseLine(string line)
        {
            string[] tokens = line.Split(',');
            PriceData r = new PriceData();
            try
            {
                int i = 0;
                r.Date = DateTime.Parse(tokens[i++]);
                r.Code = int.Parse(tokens[i++]);
                if (r.Code > 1002 && r.Code < 1300)
                    return null; // 各種指数は無視する。
                if (r.Date.Year < 2006)
                    r.Name = tokens[i++].Trim('"');
                else if (mnameTable.ContainsKey(r.Code))
                    r.Name = mnameTable[r.Code];
                if (r.Date.Year >= 2006)
                {
                    switch (int.Parse(tokens[i++]))
                    {
                        case 1:
                            r.MarketId = Db.MarketId.Tokyo1;
                            break;
                        case 2:
                            r.MarketId = Db.MarketId.Tokyo2;
                            break;
                        case 3:
                            r.MarketId = Db.MarketId.Mothers;
                            break;
                        case 4:
                            r.MarketId = Db.MarketId.Jasdaq;
                            break;
                        case 6:
                            r.MarketId = Db.MarketId.Osaka1;
                            break;
                        case 7:
                            r.MarketId = Db.MarketId.Osaka2;
                            break;
                        case 8:
                            r.MarketId = Db.MarketId.Hercules;
                            break;
                        default:
                            return null;
                    }
                }
                if (r.Name == null)
                {
                    // 2006年以降のデータでは、上場廃止で銘柄名が得られない場合がある。
                    // データベースに名前があればそれで補うが、なければデータを破棄する。
                    Brand brand = BrandTable.GetRecord(r.MarketId, r.Code);
                    if (brand == null)
                        return null;
                    r.Name = brand.Name;
                }
                
                // 指数の値に小数が含まれているのでdouble.Parseを利用する。
                r.Open = (int)double.Parse(tokens[i++]);
                r.High = (int)double.Parse(tokens[i++]);
                r.Low = (int)double.Parse(tokens[i++]);
                r.Close = (int)double.Parse(tokens[i++]);
                r.Volume = double.Parse(tokens[i++]) / 1000;
                if (r.Date.Year < 2006)
                {
                    switch (int.Parse(tokens[i++]))
                    {
                        case 1:
                            r.MarketId = Db.MarketId.Tokyo1;
                            break;
                        case 2:
                            r.MarketId = Db.MarketId.Tokyo2;
                            break;
                        case 3:
                            r.MarketId = Db.MarketId.Jasdaq;
                            break;
                        default:
                            return null;
                    }
                }
            }
            catch (FormatException)
            {
                // フォーマットエラーは全部無視する。
                return null;
            }
            return r;
        }
	}
}
