using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace StockChart
{

    class DataService
    {
        static Dictionary<string, string> _names = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        static Dictionary<string, QuoteData> _prices = new Dictionary<string, QuoteData>(StringComparer.OrdinalIgnoreCase);

        static DataService()
        {
        }

        private DataService()
        {

        }

        private static DataService _instance;
        public static DataService Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new DataService();
                }
                return _instance;
            }
        }

        public Dictionary<string, string> SymbolNames
        {
            get
            {
                // load company names (once)
                if (_names.Count == 0)
                {
                    using (FileStream fs = new FileStream(System.Environment.CurrentDirectory + @"\Resources\symbolNames.txt", FileMode.Open, FileAccess.Read))
                    {
                        using (var sr = new StreamReader(fs))
                        {
                            for (var line = sr.ReadLine(); line != null; line = sr.ReadLine())
                            {
                                var parts = line.Split('\t');
                                if (parts.Length >= 2)
                                {
                                    var key = parts[0].Trim();
                                    var value = parts[1].Trim();
                                    if (key.Length > 0 && value.Length > 0)
                                    {
                                        _names[key] = value;
                                    }
                                }
                            }
                        }
                    }

                }
                return _names;
            }
        }

        public string GetSymbolName(string symbol)
        {
            string value = null;
            if (_names != null)
            {
                if (_names.TryGetValue(symbol, out value))
                {
                    return value + "(" + symbol.ToUpper() + ")";
                }
            }
            return value;
        }

        public QuoteData GetSymbolData(string symbol, Action<string> onWebError = null)
        {
            // try getting from cache
            QuoteData quoteCache;
            if (_prices.TryGetValue(symbol, out quoteCache))
            {
                return quoteCache;
            }
            QuoteData quoteData = new QuoteData();

            // not in cache, get now
            var fmt = "https://www.quandl.com/api/v3/datasets/WIKI/{0}.csv?auth_token={1}&start_date={2}&end_date={3}";
            // 0: stock symbol
            // auth_token 1: token
            // start_date 2: date yyyy-MM-dd
            // auth_token 3: date yyyy-MM-dd
            var t = DateTime.Today;
            var token = "Y61ysYJb-SbaD6_RCstK";
            var url = string.Format(fmt, symbol, token, "2008-01-01", t.ToString("yyyy-MM-dd"));
            if (symbol == "^IXIC")
            {
                //NASDAQ COMP
                fmt = "https://www.quandl.com/api/v3/datasets/13110742/data.csv?auth_token={0}&start_date={1}&end_date={2}";
                url = string.Format(fmt, token, "2008-01-01", t.ToString("yyyy-MM-dd"));
            }


            // get content
            var sb = new StringBuilder();
            var wc = new System.Net.WebClient();

            try
            {
                using (var sr = new StreamReader(wc.OpenRead(url)))
                {
                    // skip headers
                    sr.ReadLine();
                    sr.ReadLine();

                    // read each line
                    for (var line = sr.ReadLine(); line != null; line = sr.ReadLine())
                    {
                        // append date (field 0) and adjusted close price (field 6)
                        var items = line.Split(',');

                        if (symbol == "^IXIC")
                        {
                            Quote q = new Quote(quoteData.ReferenceValue)
                            {
                                date = DateTime.Parse(items[0]),
                                open = Convert.ToDouble(items[1]),
                                high = Convert.ToDouble(items[2]),
                                low = Convert.ToDouble(items[3]),
                                close = Convert.ToDouble(items[1]),
                                volume = 0,
                            };
                            quoteData.Add(q);
                        }
                        else
                        {
                            if (items[0] == "2008-01-02")
                            {

                            }
                            Quote q = new Quote(quoteData.ReferenceValue)
                            {

                                date = DateTime.Parse(items[0]),
                                open = Convert.ToDouble(items[1]),
                                high = Convert.ToDouble(items[2]),
                                low = Convert.ToDouble(items[3]),
                                close = Convert.ToDouble(items[4]),
                                volume = Convert.ToDouble(items[5]),
                            };
                            quoteData.Add(q);
                        }
                    }
                }
            }
            catch (System.Net.WebException ex)
            {
                if (onWebError != null)
                {
                    onWebError(ex.Message);
                }
                else
                {
                    throw ex;
                }
            }
            if (symbol != "^IXIC")
            {
                FillEvents(symbol, quoteData);
            }

            quoteData.Reverse();
            _prices[symbol] = quoteData;

            return quoteData;
        }


        public void GetSymbolDataAsync(string symbol, Action<QuoteData> onCallback, Action<string> onWebError = null)
        {
            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback((oo) =>
            {
                var data = GetSymbolData(symbol, onWebError);
                if (onCallback != null)
                {
                    onCallback(data);
                }
            }));
        }

        private void FillEvents(string symbol, IEnumerable<Quote> qs)
        {
            // not in cache, get now
            var fmt = "https://www.google.com/finance/company_news?q={0}&output=rss&start=1&num=100";
            var url = string.Format(fmt, symbol);

            // get content
            var wc = new System.Net.WebClient();

            using (var sr = new StreamReader(wc.OpenRead(url)))
            {
                // read each line
                for (var line = sr.ReadLine(); line != null; line = sr.ReadLine())
                {
                    XDocument doc = XDocument.Parse(sr.ReadToEnd().Trim());

                    foreach (XElement c in doc.Descendants("item"))
                    {
                        DateTime dt = DateTime.Parse((string)c.Element("pubDate"));
                        var text = (string)c.Element("title");

                        var quote = qs.FirstOrDefault(q => (q.date - dt).Days == 0);

                        if (quote != null)
                        {
                            if (quote.events != null)
                            {
                                if (quote.events.Length > 0)
                                {
                                    quote.events += Environment.NewLine;
                                }
                                quote.events += text;
                            }
                            else
                            {
                                quote.events = text;
                            }
                        }
                    }
                }
            }
        }

        public QuoteRange GetSymbolYRange(IEnumerable<Quote> quoteData, double s, double e, string porperty = null)
        {
            QuoteRange qr = null;
            DateTime ds = DateTime.FromOADate(s);
            DateTime de = DateTime.FromOADate(e);
            IEnumerable<Quote> quoteCache = from p in quoteData where p.date >= ds && p.date <= de select p;

            if (quoteCache.Any())
            {
                qr = new QuoteRange();
                qr.PriceMin = quoteCache.Min((k) =>
                {
                    if (string.IsNullOrEmpty(porperty) || porperty.ToUpper() == "high,low,open,close".ToUpper())
                    {
                        return k.low;
                    }
                    else
                    {
                        return k.GetValue(porperty);
                    }
                });
                qr.PriceMax = quoteCache.Max((k) =>
                {
                    if (string.IsNullOrEmpty(porperty) || porperty.ToUpper() == "high,low,open,close".ToUpper())
                    {
                        return k.high;
                    }
                    else
                    {
                        return k.GetValue(porperty);
                    }
                });
                qr.VolumeMin = quoteCache.Min(k => k.volume);
                qr.VolumeMax = quoteCache.Max(k => k.volume);
            }
            return qr;
        }


    }
}
