Imports System.IO
Imports System.Net.Http
Imports System.Web
Imports System.Xml

Class ElectionDayResults
    Public Property ParishVotes As XmlDocument
    Public Property PrecinctVotes As XmlDocument
End Class

Class ElectionResultsReader
    Private Const BASE_API_ADDRESS As String = "https://voterportal.sos.la.gov/api/MediaRequests/"
    Private Const FORMATTED_ELECTION_DATE As String = "2017-03-25"
    Private Const ACCESS_KEY As String = "<Your Access Key Here>"
    Private Const SECRET_KEY As String = "<Your Secret Key Here>"

    Private Shared ReadOnly httpClient As HttpClient
    Private Shared latestSavedVersion As Date?


    Shared Sub New()
        httpClient = New HttpClient() With {
            .BaseAddress = New Uri(BASE_API_ADDRESS)
            }
    End Sub

    ''' <summary>
    ''' Returns an XmlDocument with a list of races and candidates for the election.
    ''' </summary>
    ''' <remarks>This data does not change often and should be requested infrequently.</remarks>
    Public Shared Async Function ReadRacesAndCandidates() As Task(Of XmlDocument)
        Return Await GetXmlFile("RacesAndCandidates")
    End Function

    ''' <summary>
    ''' If new election day results are available, returns them as a pair of XML documents: 
    ''' ParishVotes and PrecinctVotes. If new results are not available, returns null.
    ''' </summary>
    Public Shared Async Function ReadElectionDayResults(includePrecinctLevelVotes As Boolean) As Task(Of ElectionDayResults)
        Dim results As ElectionDayResults = Nothing

        ' Determine if a new version of the election results is available.
        Dim currentResultsVersion As Date? = Await GetCurrentResultsVersion()
        Dim isNewVersion As Boolean = currentResultsVersion.HasValue AndAlso
                                      (latestSavedVersion Is Nothing OrElse currentResultsVersion.Value > latestSavedVersion.Value)

        ' Only request election results when there is a new version. This avoids unnecessary API requests.
        If isNewVersion Then
            latestSavedVersion = currentResultsVersion

            ' Request Election Results
            Dim parishVotesXml As XmlDocument = Await GetXmlFile("ParishVotes")
            Dim precinctVotesXml As XmlDocument = If(includePrecinctLevelVotes, Await GetXmlFile("PrecinctVotes"), Nothing)

            ' Bundle Election Results
            results = New ElectionDayResults() With {
                .ParishVotes = parishVotesXml,
                .PrecinctVotes = precinctVotesXml
                }
        End If

        Return results
    End Function


    Private Shared Async Function GetXmlFile(fileName As String) As Task(Of XmlDocument)
        ' Request XML File
        Dim requestUrl = String.Format("{0}/{1}/{2}/{3}", fileName, FORMATTED_ELECTION_DATE, ACCESS_KEY, SECRET_KEY)
        Dim response As HttpResponseMessage = Await httpClient.GetAsync(requestUrl)

        ' If the request was not successful, throw an exception detailing why.
        If Not response.IsSuccessStatusCode Then
            Dim responseContents = response.Content.ReadAsStringAsync().Result
            Dim errorMessage As String = String.Format("{0} ({1}): {2}",
                                                       response.StatusCode, CInt(response.StatusCode), responseContents)
            Throw New HttpException(response.StatusCode, errorMessage)
        End If

        ' Extract XML from HttpResponse
        Dim xmlStream As Stream = Await response.Content.ReadAsStreamAsync()
        Dim xmlFile As New XmlDocument()
        xmlFile.Load(xmlStream)

        Return xmlFile
    End Function

    Private Shared Async Function GetCurrentResultsVersion() As Task(Of Date?)
        Dim currentResultsVersion As Date? = Nothing

        ' Retrieve XML File Containing Version Information
        Dim latestVersionXml As XmlDocument = Await GetXmlFile("LatestVersion")

        ' Retrieve datetime string from version XML.
        Dim dateString As String = If(latestVersionXml("Version") IsNot Nothing, latestVersionXml("Version").InnerText, Nothing)

        ' Attempt to parse a date from the XML contents.
        If Not String.IsNullOrWhiteSpace(dateString) Then
            Dim version As Date
            If Date.TryParse(dateString, version) Then
                currentResultsVersion = version
            End If
        End If

        Return currentResultsVersion
    End Function
End Class