Imports System.Text

Class DataService
    Shared _names As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
    Shared _prices As New Dictionary(Of String, QuoteData)(StringComparer.OrdinalIgnoreCase)

    Shared Sub New()
    End Sub


    Private Sub New()
    End Sub

    Private Shared _instance As DataService
    Public Shared ReadOnly Property Instance() As DataService
        Get
            If _instance Is Nothing Then
                _instance = New DataService()
            End If
            Return _instance
        End Get
    End Property

    Public ReadOnly Property SymbolNames() As Dictionary(Of String, String)
        Get
            ' load company names (once)
            If _names.Count = 0 Then
                For Each line As String In DataPort.SymbolNames
                    If line IsNot Nothing Then
                        Dim parts = line.Split(vbTab)
                        If parts.Length >= 2 Then
                            Dim key = parts(0).Trim()
                            Dim value = parts(1).Trim()
                            If key.Length > 0 AndAlso value.Length > 0 Then
                                _names(key) = value
                            End If
                        End If
                    End If
                Next
            End If
            Return _names
        End Get
    End Property

    Public Function GetSymbolName(symbol As String) As String
        Dim value As String = Nothing
        If _names IsNot Nothing Then
            If _names.TryGetValue(symbol, value) Then
                Return (value & Convert.ToString("(")) + symbol.ToUpper() + ")"
            End If
        End If
        Return value
    End Function

    Public Function GetSymbolData(symbol As String, Optional onWebError As Action(Of String) = Nothing) As QuoteData
        ' try getting from cache
        Dim quoteCache As QuoteData
        If _prices.TryGetValue(symbol, quoteCache) Then
            Return quoteCache
        End If
        Dim quoteData As New QuoteData()

        ' not in cache, get now
        Dim 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
        Dim t = DateTime.Today
        Dim token = "Y61ysYJb-SbaD6_RCstK"
        Dim url = String.Format(fmt, symbol, token, "2008-01-01", t.ToString("yyyy-MM-dd"))
        If symbol = "^IXIC" Then
            '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"))
        End If


        ' get content
        Dim sb = New StringBuilder()
        Dim request = System.Net.WebRequest.Create(url)


        Dim task = request.GetResponseAsync()
        'where it's exiting
        Try
            Using sr = New StreamReader(task.Result.GetResponseStream())
                ' skip headers
                sr.ReadLine()

                ' read each line
                Dim line = sr.ReadLine()
                While line IsNot Nothing
                    ' append date (field 0) and adjusted close price (field 6)
                    Dim items = line.Split(","c)

                    If symbol = "^IXIC" Then
                        Dim q As New Quote(quoteData.ReferenceValue) With {
                           .[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" Then
                        End If

                        Dim q As New Quote(quoteData.ReferenceValue) With {
                           .[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)
                    End If
                    line = sr.ReadLine()
                End While
            End Using
        Catch ex As AggregateException
            If onWebError IsNot Nothing Then
                onWebError(ex.Message)
            Else
                System.Diagnostics.Debug.WriteLine(ex.Message)
            End If
        Catch ex As System.Net.WebException
            If onWebError IsNot Nothing Then
                onWebError(ex.Message)
            Else
                System.Diagnostics.Debug.WriteLine(ex.Message)
            End If
        End Try
        If symbol <> "^IXIC" Then
            FillEvents(symbol, quoteData)
        End If

        quoteData.Reverse()
        _prices(symbol) = quoteData

        Return quoteData
    End Function

    Public Async Sub GetSymbolDataAsync(symbol As String, onCallback As Action(Of QuoteData), Optional onWebError As Action(Of String) = Nothing)
        Await Windows.System.Threading.ThreadPool.RunAsync(Sub(workItem As Object)
                                                               Dim data = GetSymbolData(symbol, onWebError)
                                                               onCallback(data)
                                                           End Sub
                                                           )
    End Sub



    Private Sub FillEvents(symbol As String, qs As IEnumerable(Of Quote))
        ' not in cache, get now
        Dim fmt = "https://www.google.com/finance/company_news?q={0}&output=rss&start=1&num=100"
        Dim url = String.Format(fmt, symbol)

        ' get content
        Dim sb = New StringBuilder()

        Try
            Dim request = System.Net.WebRequest.Create(url)

            Dim task = request.GetResponseAsync()
            'where it's exiting
            Using sr = New StreamReader(task.Result.GetResponseStream())
                ' read each line
                Dim line = sr.ReadLine()
                While line IsNot Nothing
                    Dim doc As XDocument = XDocument.Parse(sr.ReadToEnd().Trim())

                    For Each c As XElement In doc.Descendants("item")
                        Dim pubDate As String = CType(c.Element("pubDate"), String)
                        Dim dt As DateTime = DateTime.Parse(pubDate)
                        Dim text = CType(c.Element("title"), String)

                        Dim quote = qs.FirstOrDefault(Function(q) (q.[date] - dt).Days = 0)
                        If quote IsNot Nothing Then
                            If Not String.IsNullOrEmpty(quote.events) Then
                                quote.events += Environment.NewLine
                            End If
                            quote.events += text
                        End If

                    Next
                    line = sr.ReadLine()
                End While
            End Using
        Catch ex As Exception
            System.Diagnostics.Debug.WriteLine(ex.Message)
        End Try

    End Sub

    Public Function GetSymbolYRange(quoteData As IEnumerable(Of Quote), s As Double, e As Double, Optional porperty As String = Nothing) As QuoteRange
        Dim qr As QuoteRange = Nothing
        Dim ds As DateTime = Utilities.FromOADate(s)
        Dim de As DateTime = Utilities.FromOADate(e)
        Dim quoteCache As IEnumerable(Of Quote) = From p In quoteData Where p.[date] >= ds AndAlso p.[date] <= de Select p

        If quoteCache.Any() Then
            qr = New QuoteRange()
            qr.PriceMin = quoteCache.Min(Function(k)
                                             If String.IsNullOrEmpty(porperty) OrElse porperty.ToUpper() = "high,low,open,close".ToUpper() Then
                                                 Return k.low
                                             Else
                                                 Return k.GetValue(porperty)
                                             End If

                                         End Function)
            qr.PriceMax = quoteCache.Max(Function(k)
                                             If String.IsNullOrEmpty(porperty) OrElse porperty.ToUpper() = "high,low,open,close".ToUpper() Then
                                                 Return k.high
                                             Else
                                                 Return k.GetValue(porperty)
                                             End If

                                         End Function)
            qr.VolumeMin = quoteCache.Min(Function(k) k.volume)
            qr.VolumeMax = quoteCache.Max(Function(k) k.volume)
        End If
        Return qr
    End Function
End Class