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();

            DateTime t = DateTime.Today;
            Stream dataStream = null;
            StreamWriter dataCacheStream = null;

            // check the file cache as well
            FileInfo fileInfo = new FileInfo(symbol + ".dataCache");
            if (fileInfo.Exists && fileInfo.LastWriteTime.Date == t)
            {
                try { dataStream = fileInfo.OpenRead(); }
                catch { dataStream = null; }
            }

            // not in cache, get now
            if(dataStream == null)
            {
                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 token = "CyzcmQUSUh7Xg5cvroac";
                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"));
                }
                try
                {
                    var wc = new System.Net.WebClient();
                    dataStream = wc.OpenRead(url);
                    dataCacheStream = fileInfo.CreateText();
                }
                catch { dataStream = null; }
            }

            if (dataStream == null)
            {
                string msg = "Data for \"" + symbol + "\" not available.";
                if (fileInfo.Exists)
                {
                    try
                    {
                        dataStream = fileInfo.OpenRead();
                        msg = "New Data for \"" + symbol + "\" not available.\r\n" +
                              "Using cached data from " +
                              fileInfo.LastWriteTime.Date.ToShortDateString();
                    }
                    catch
                    {
                        dataStream = null;
                    }
                }
                if (onWebError != null)
                    onWebError(msg);
                else if(dataStream == null)
                    throw new Exception(msg);
                if (dataStream == null) return quoteData;
            }

            try
            {
                using (var sr = new StreamReader(dataStream))
                {
                    // skip headers
                    if (dataCacheStream != null)
                    {
                        dataCacheStream.WriteLine(sr.ReadLine());
                        dataCacheStream.WriteLine(sr.ReadLine());
                    }
                    else
                    {
                        sr.ReadLine();
                        sr.ReadLine();
                    }

                    // read each line
                    for (var line = sr.ReadLine(); line != null; line = sr.ReadLine())
                    {
                        if (dataCacheStream != null) dataCacheStream.WriteLine(line);

                        // 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
                        {
                            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;
                }
            }
            finally
            {
                if (dataCacheStream != null)
                    dataCacheStream.Close();
            }

            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 = "http://articlefeeds.nasdaq.com/nasdaq/symbols?symbol={0}";
            var url = string.Format(fmt, symbol);

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

            using (var stream = wc.OpenRead(url))
            {
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.Load(stream);

                var items = doc.GetElementsByTagName("item");
                // read each line
                foreach (System.Xml.XmlNode item in items)
                {
                    var dt = DateTime.Parse(item["pubDate"].InnerText);
                    var text = item["title"].InnerText;
                    
                    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;
        }


    }
}
