// Copyright (c) 2014 panacoran <panacoran@users.sourceforge.jp>
// This program is part of OmegaChart.
// OmegaChart is licensed under the Apache License, Version 2.0.

using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using Zanetti.Data;

namespace Zanetti.DataSource.Specialized
{
    internal class EdatalabDataSource : DailyDataSource
    {
        public EdatalabDataSource(int[] dates) : base(dates)
        {
        }

        public override void Run()
        {
            var newdata = new Dictionary<int, Dictionary<int, NewDailyData>>();
            foreach (var date in _dates)
            {
                newdata[date] = FillData(date);
                SendMessage(AsyncConst.WM_ASYNCPROCESS, (date | DATE_MASK), AsyncConst.LPARAM_PROGRESS_SUCCESSFUL);
            }
            foreach (AbstractBrand br in Env.BrandCollection.Values)
            {
                if (br.Market == MarketType.B && !IsIndex(br.Code) || br.Market == MarketType.Custom)
                    continue;
                using (var farm = (DailyDataFarm)br.CreateDailyFarm(_dates.Length))
                {
                    var traceFlag = false;
                    foreach (var date in _dates)
                    {
                        NewDailyData td;
                        if (newdata[date].TryGetValue(br.Code, out td))
                        {
                            farm.UpdateDataFarm(date, td);
                        }
                        else
                        {
                            if (traceFlag)
                                continue;
                            traceFlag = true;
                            Debug.WriteLine("Data not found(edatalab.net) : code=" + br.Code + " market=" + br.Market);
                        }
                    }
                    farm.Save(Util.GetDailyDataFileName(br.Code));
                    SendMessage(AsyncConst.WM_ASYNCPROCESS, br.Code, AsyncConst.LPARAM_PROGRESS_SUCCESSFUL);
                }
            }
        }

        private Dictionary<int, NewDailyData> FillData(int date)
        {
            var result = new Dictionary<int, NewDailyData>();
            var d2 = Util.IntToDate(date);
            var url = "http://www.edatalab.sakura.ne.jp/data" +
                      d2.ToString(d2.Year < 2006 ? "yyyy/yyyyMMdd" : "yyyy/DyyMMdd") + ".LZH";
            using (var reader = new StreamReader(Util.ExtractData(url), Encoding.GetEncoding("shift_jis")))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    var tokens = line.Split(',');
                    var code = int.Parse(tokens[1]);
                    if (code > 1002 && code < 1300) // 各種指数を無視する。
                        continue;
                    code = code == 1001 ? (int)BuiltInIndex.Nikkei225 : code == 1002 ? (int)BuiltInIndex.TOPIX : code;
                    var br = Env.BrandCollection.FindBrand(code) as BasicBrand;
                    if (br == null ||
                        (d2.Year < 2006 ? !CheckMarketOld(br.Market, tokens[8]) : !CheckMarket(br.Market, tokens[2])))
                        continue;
                    var vv = IsIndex(code) ? 0.001 : 1;
                    var pv = IsIndex(code) ? 100 : 10; // 指数は100倍、株価は10倍で記録。
                    result[code] = new NewDailyData
                    {
                        open = (int)(double.Parse(tokens[3]) * pv),
                        high = (int)(double.Parse(tokens[4]) * pv),
                        low = (int)(double.Parse(tokens[5]) * pv),
                        close = (int)(double.Parse(tokens[6]) * pv),
                        volume = (int)(double.Parse(tokens[7]) * vv),
                    };
                }
            }
            return result;
        }

        private bool CheckMarketOld(MarketType market, string name)
        {
            switch (int.Parse(name))
            {
                case 1:
                    return market == MarketType.T1 ||
                           market == MarketType.B; // 指数は東証1部
                case 2:
                    return market == MarketType.T2;
                case 3:
                    return market == MarketType.J;
            }
            return false;
        }

        private bool CheckMarket(MarketType market, string name)
        {
            switch (int.Parse(name))
            {
                case 1:
                    return market == MarketType.T1 ||
                           market == MarketType.B; // 指数は東証1部
                case 2:
                    return market == MarketType.T2;
                case 3:
                    return market == MarketType.M;
                case 4:
                    return market == MarketType.J;
                case 6:
                    return market == MarketType.O1;
                case 7:
                    return market == MarketType.O2;
                case 8:
                    return market == MarketType.H;
            }
            return false;
        }

        private bool IsIndex(int code)
        {
            return code == (int)BuiltInIndex.Nikkei225 ||
                   code == (int)BuiltInIndex.TOPIX;
        }
    }
}