/// <summary>
        /// Determines the direction of the line and flips it neccesary.
        /// </summary>
        /// <returns>Whether the direction could be determined</returns>
        private bool determineDirection()
        {
            double firstDist = double.NaN;

            foreach (StationInfo station in stations)
            {
                if (station.IntDistance != -1)
                {
                    Station st = DatabaseLegacy.GetStation(station.Name);
                    double  dist;
                    if (st != null && !double.IsNaN(dist = Polyline.GetProjectedDistance(WebMercator.DefaultMap.FromLatLon(st.GPSCoord), 0.05)))
                    {
                        if (double.IsNaN(firstDist))
                        {
                            firstDist = dist;
                        }
                        else //if (double.IsNaN(secondDist))
                        {
                            if (dist < firstDist)
                            {
                                Polyline = new Polyline(Polyline.Points.Reverse().ToList());
                            }

                            return(true);
                        }
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Updates fields based on a TRAIN API JSON request
        /// </summary>
        /// <param name="apiResponse">JSON TRAIN MÁV API response</param>
        public void UpdateTRAIN_API(JObject apiResponse)
        {
            HtmlDocument trainHTML = new HtmlDocument();

            trainHTML.LoadHtml(WebUtility.HtmlDecode((string)apiResponse["d"]["result"]["html"]));
            HtmlNode table;

            try
            {
                table = trainHTML.DocumentNode.Descendants("table").Where(tb => tb.HasClass("vt")).First();
            }
            catch (InvalidOperationException e)
            {
                throw new MAVAPIException("Cannot parse train table.");
            }

            string fix2400Time(string s)
            {
                if (s.Trim() == "24:00")
                {
                    return("0:00");
                }
                else
                {
                    return(s);
                }
            }

            try
            {
                // The header containing train type and name information
                IEnumerable <HtmlNode> tableHeader = table.Descendants("th").First().Descendants();
                string[] name = tableHeader.First().InnerHtml.Split(new char[] { ' ', '\r', '\n', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                Number = name[0];

                // Figuring out train type
                if (name.Length > 1)
                {
                    if (name[1].Trim().ToLower() == "sebesvonat" || name[1].Trim().ToLower() == "gyorsvonat" || name[1].Trim().ToLower() == "személyvonat" ||
                        name[1].Trim().ToLower() == "EuroRegio" || name[1].Trim().ToLower() == "InterCity" || name[1].Trim().ToLower() == "EuroCity" ||
                        name[1].Trim().ToLower() == "vonatpótló autóbusz" || name[1].Trim().ToLower() == "railjet")
                    {
                        Type = name[1].Trim().ToLower();
                    }
                    else
                    {
                        Name = name[1];
                    }
                }

                bool ul = false;

                foreach (HtmlNode node in tableHeader)
                {
                    // This is a sort of secondary train type for specific trains
                    if (node.HasClass("viszszam2"))
                    {
                        NumberType = node.InnerHtml;
                    }
                    // Sometimes train type can only be decuded from the alt of an image
                    else if (node.Name == "img" && !ul)
                    {
                        Type = node.Attributes["alt"].Value;
                    }
                    // Sometimes trains can be broken down into multiple types
                    else if (node.Name == "ul")
                    {
                        ul   = true;
                        Type = "";
                        foreach (HtmlNode li in node.Descendants("li"))
                        {
                            string type = "";
                            type = Regex.Replace(li.Descendants().First().InnerHtml, @"\s+", " ");
                            IEnumerable <HtmlNode> img = li.Descendants().Where(d => d.Name == "img");
                            if (img != null && img.Any())
                            {
                                type += " " + img.First().Attributes["alt"].Value;
                            }
                            Type += (Type == "" ? "" : "\n") + type;
                        }
                    }
                }

                // Any information that might be helpful
                miscInfo.Clear();
                IEnumerable <HtmlNode> miscInform = table.Descendants("th").Skip(1);
                foreach (HtmlNode node in miscInform)
                {
                    // The actual table headers start which are uninteresting
                    if (node.InnerHtml == "Km")
                    {
                        break;
                    }

                    // Anything starting with this text is redundant with another API's information
                    if (node.InnerHtml.StartsWith("A pillanatnyi késés"))
                    {
                        continue;
                    }

                    // Anything containing the word delay is related to delays (this might fail)
                    else if (node.InnerHtml.Contains("késés"))
                    {
                        DelayReason = node.InnerHtml;
                    }
                    // The truly misc info
                    else
                    {
                        miscInfo.Add(node.InnerHtml);
                    }
                }
            }
            catch (InvalidOperationException e)
            {
                throw new MAVAPIException("Cannot parse train header.");
            }

            if ((apiResponse["d"]["result"]["line"] as JArray).Count == 0)
            {
                throw new MAVAPIException("Cannot get train line.");
            }

            bool hasTrainData = HasTRAINData; // Polyline will be defined in a second so that would skew our method
            // Polyline of the train path
            List <Vector2> points = Polyline.DecodePoints(apiResponse["d"]["result"]["line"][0]["points"].ToString(), 1E5f);

            Polyline = new Polyline(points);

            // Station infos
            IEnumerable <HtmlNode> stationRows = table.Descendants("tr").Where(tr => tr.Attributes.Contains("class"));

            if (hasTrainData) // If we already have train data then stations will be updated
            {
                int stationCnt = 0;
                foreach (HtmlNode stationRow in stationRows)
                {
                    bool highlighted = stationRow.Attributes["class"].Value.StartsWith("row_past");

                    // Skip invalid rows
                    if (stationRow.Descendants("td").Count() < 4)
                    {
                        continue;
                    }

                    IEnumerator <HtmlNode> tdEnumerator = stationRow.Descendants("td").GetEnumerator();
                    tdEnumerator.MoveNext();                                                   // On integer distance cell
                    tdEnumerator.MoveNext();                                                   // On station name cell
                    tdEnumerator.MoveNext();                                                   // On arrival time cell

                    TimeSpan expectedArrival = stations[stationCnt].ExpectedArrival.TimeOfDay; //Don't change if there's an error parsing
                    try
                    {
                        if (stationRows.First() != stationRow)
                        {
                            // Arrival time parsing block
                            {
                                IEnumerator <HtmlNode> timeEnumerator = tdEnumerator.Current.Descendants().GetEnumerator();
                                timeEnumerator.MoveNext();     // On the first text block contatining scheduled arrival
                                if (timeEnumerator.MoveNext()) // On a <br>
                                {
                                    timeEnumerator.MoveNext(); // On the second text block in a span containing expected arrival
                                    expectedArrival = TimeSpan.ParseExact(fix2400Time(timeEnumerator.Current.InnerHtml), "g", null);
                                }
                            }
                        }
                    }
                    catch (FormatException e)
                    {
                        throw new MAVAPIException("Cannot parse train arrival time.");
                    }

                    tdEnumerator.MoveNext();                                                       // On departure time cell

                    TimeSpan expectedDeparture = stations[stationCnt].ExpectedDeparture.TimeOfDay; //Don't change if there's an error parsing
                    try
                    {
                        if (stationRows.Last() != stationRow)
                        {
                            // Departure time parsing block
                            {
                                IEnumerator <HtmlNode> timeEnumerator = tdEnumerator.Current.Descendants().GetEnumerator();
                                timeEnumerator.MoveNext();     // On the first text block contatining scheduled departure
                                if (timeEnumerator.MoveNext()) // On a <br>
                                {
                                    timeEnumerator.MoveNext(); // On the second text block in a span containing expected departure
                                    expectedDeparture = TimeSpan.ParseExact(fix2400Time(timeEnumerator.Current.InnerHtml), "g", null);
                                }
                            }
                        }
                    }
                    catch (FormatException e)
                    {
                        throw new MAVAPIException("Cannot parse train departure time.");
                    }

                    string platform = null;
                    if (tdEnumerator.MoveNext()) // There might not even be a cell for platforms
                    {
                        // On platform cell
                        platform = tdEnumerator.Current.InnerHtml.Trim();
                    }

                    DateTime expectedArrivalDateTime = stations[stationCnt].Arrival.Date + expectedArrival, expectedDepartureDateTime = stations[stationCnt].Departure.Date + expectedDeparture;

                    if (stations[stationCnt].Arrival.TimeOfDay > expectedArrival) // we are on the border of this day
                    {
                        expectedArrivalDateTime = stations[stationCnt].Arrival.Date.AddDays(1) + expectedArrival;
                    }

                    if (stations[stationCnt].Departure.TimeOfDay > expectedDeparture) // we are on the border of this day
                    {
                        expectedDepartureDateTime = stations[stationCnt].Departure.Date.AddDays(1) + expectedDeparture;
                    }

                    stations[stationCnt++].Update(expectedArrivalDateTime, expectedDepartureDateTime, highlighted || (expectedDepartureDateTime <= DateTime.Now), platform == "" ? null : platform);
                }
            }
            else // If we don't have train data already then stations must be added, and the distance information also has to be calculated
            {
                foreach (HtmlNode stationRow in stationRows)
                {
                    bool highlighted = stationRow.Attributes["class"].Value.StartsWith("row_past");

                    // Skip invalid rows
                    if (stationRow.Descendants("td").Count() < 4)
                    {
                        continue;
                    }

                    IEnumerator <HtmlNode> tdEnumerator = stationRow.Descendants("td").GetEnumerator();
                    tdEnumerator.MoveNext(); // On integer distance cell
                    int intDistance = -1;
                    if (tdEnumerator.Current.InnerHtml.Trim() != "")
                    {
                        try
                        {
                            intDistance = int.Parse(tdEnumerator.Current.InnerHtml);
                        }
                        catch (FormatException e)
                        {
                            throw new MAVAPIException("Cannot parse train station distance cell.");
                        }
                    }

                    tdEnumerator.MoveNext(); // On station name cell
                    string   stationName = tdEnumerator.Current.InnerText;
                    DateTime date        = new DateTime(0);
                    try
                    {
                        date = DateTime.ParseExact(Regex.Match(tdEnumerator.Current.Descendants("a").First().Attributes["onclick"].Value, "d: '(?<date>[^' ]*?)'").Groups["date"].Value, "yy.MM.dd", null);
                    }
                    catch (FormatException e)
                    {
                        throw new MAVAPIException("Cannot parse train date.");
                    }

                    tdEnumerator.MoveNext(); // On arrival time cell

                    TimeSpan arrival = new TimeSpan(0, 0, 0), expectedArrival = new TimeSpan(0, 0, 0);
                    try
                    {
                        if (stationRows.First() != stationRow)
                        {
                            // Arrival time parsing block
                            {
                                IEnumerator <HtmlNode> timeEnumerator = tdEnumerator.Current.Descendants().GetEnumerator();
                                timeEnumerator.MoveNext();     // On the first text block contatining scheduled arrival
                                arrival = TimeSpan.ParseExact(fix2400Time(timeEnumerator.Current.InnerHtml), "g", null);
                                if (timeEnumerator.MoveNext()) // On a <br>
                                {
                                    timeEnumerator.MoveNext(); // On the second text block in a span containing expected arrival
                                    expectedArrival = TimeSpan.ParseExact(fix2400Time(timeEnumerator.Current.InnerHtml), "g", null);
                                }
                                else
                                {
                                    expectedArrival = arrival;
                                }
                            }
                        }
                    }
                    catch (FormatException e)
                    {
                        throw new MAVAPIException("Cannot parse train arrival time.");
                    }

                    tdEnumerator.MoveNext(); // On departure time cell

                    TimeSpan departure = new TimeSpan(0, 0, 0), expectedDeparture = new TimeSpan(0, 0, 0);
                    try
                    {
                        if (stationRows.Last() != stationRow)
                        {
                            // Departure time parsing block
                            {
                                IEnumerator <HtmlNode> timeEnumerator = tdEnumerator.Current.Descendants().GetEnumerator();
                                timeEnumerator.MoveNext();     // On the first text block contatining scheduled departure
                                departure = TimeSpan.ParseExact(fix2400Time(timeEnumerator.Current.InnerHtml), "g", null);
                                if (timeEnumerator.MoveNext()) // On a <br>
                                {
                                    timeEnumerator.MoveNext(); // On the second text block in a span containing expected departure
                                    expectedDeparture = TimeSpan.ParseExact(fix2400Time(timeEnumerator.Current.InnerHtml), "g", null);
                                }
                                else
                                {
                                    expectedDeparture = departure;
                                }
                            }
                        }
                    }
                    catch (FormatException e)
                    {
                        throw new MAVAPIException("Cannot parse train departure time.");
                    }

                    string platform = null;
                    if (tdEnumerator.MoveNext()) // There might not even be a cell for platforms
                    {
                        // On platform cell
                        platform = tdEnumerator.Current.InnerHtml.Trim();
                    }

                    DateTime arrivalDateTime = date + arrival, departureDateTime = date + departure, expectedArrivalDateTime = date + expectedArrival, expectedDepartureDateTime = date + expectedDeparture;

                    if (arrival > departure) // we are on the border of this day
                    {
                        departureDateTime = date.AddDays(1) + departure;
                    }

                    if (arrival > expectedArrival) // we are on the border of this day
                    {
                        expectedArrivalDateTime = date.AddDays(1) + expectedArrival;
                    }

                    if (arrival > expectedDeparture) // we are on the border of this day
                    {
                        expectedDepartureDateTime = date.AddDays(1) + expectedDeparture;
                    }

                    StationInfo stationInfo = new StationInfo(stationName, intDistance, arrivalDateTime, departureDateTime, expectedArrivalDateTime, expectedDepartureDateTime,
                                                              highlighted || (expectedDepartureDateTime <= DateTime.Now), platform == "" ? null : platform);
                    stations.Add(stationInfo);
                }


                if (determineDirection())
                {
                    int firstFound = -1;
                    for (int i = 0; i < stations.Count; i++)
                    {
                        if (stations[i].IntDistance != -1)
                        {
                            Station st = DatabaseLegacy.GetStation(stations[i].Name);
                            if (st != null)
                            {
                                stations[i].UpdateStation(st);
                            }

                            double dist = 0;
                            if (st != null && !double.IsNaN(dist = Polyline.GetProjectedDistance(WebMercator.DefaultMap.FromLatLon(st.GPSCoord), 0.25)))
                            {
                                stations[i].UpdateDistanceInformation(dist, StationPositionAccuracy.Precise);
                                if (firstFound == -1)
                                {
                                    firstFound = i;
                                }
                            }
                            else if (!double.IsNaN(dist) && i > 0 && stations[i - 1].PositionAccuracy != StationPositionAccuracy.Missing)
                            {
                                stations[i].UpdateDistanceInformation(stations[i - 1].Distance + (stations[i].IntDistance - stations[i - 1].IntDistance), StationPositionAccuracy.IntegerPrecision);
                            }
                        }
                    }

                    for (int i = firstFound - 1; i >= 0; i--) // We do a reverse check as well so that we can figure out missing stations backwards from the first found
                    {
                        if (stations[i].IntDistance != -1 &&
                            (stations[i].Station != null && !double.IsNaN(Polyline.GetProjectedDistance(WebMercator.DefaultMap.FromLatLon(stations[i].Station.GPSCoord), 0.05))) &&
                            stations[i].PositionAccuracy == StationPositionAccuracy.Missing)
                        {
                            stations[i].UpdateDistanceInformation(stations[i + 1].Distance - (stations[i + 1].IntDistance - stations[i].IntDistance), StationPositionAccuracy.IntegerPrecision);
                        }
                    }
                }
            }
        }