/// <summary>
 /// Trnasfers the core weather observation data
 /// </summary>
 /// <param name="dto"></param>
 /// <param name="xml"></param>
 private void ParseCurrentWeatherData(METARDto dto, METAR xml)
 {
     dto.Altimeter        = xml.altim_in_hg;
     dto.Dewpoint         = ParserHelpers.GetValue(xml.dewpoint_cSpecified, xml.dewpoint_c);
     dto.ObsTime          = ParserHelpers.ParseDateTime(xml.observation_time);
     dto.Precipitation    = ParserHelpers.GetValue(xml.precip_inSpecified, xml.precip_in);
     dto.Snow             = ParserHelpers.GetValue(xml.snow_inSpecified, xml.snow_in);
     dto.RawMETAR         = xml.raw_text;
     dto.SeaLevelPressure = ParserHelpers.GetValue(xml.sea_level_pressure_mbSpecified, xml.sea_level_pressure_mb);
     if (xml.sky_condition != null)
     {
         dto.SkyCondition.AddRange(xml.sky_condition.Select(sc => new SkyConditionDto()
         {
             SkyCondition = SkyConditionType.ByName(sc.sky_cover),
             CloudBase    = sc.cloud_base_ft_agl
         }));
     }
     dto.Temperature        = ParserHelpers.GetValue(xml.temp_cSpecified, xml.temp_c);
     dto.VerticalVisibility = ParserHelpers.GetValue(xml.vert_vis_ftSpecified, xml.vert_vis_ft);
     dto.Visibility         = ParserHelpers.GetValue(xml.visibility_statute_miSpecified, xml.visibility_statute_mi);
     dto.Weather            = xml.wx_string;
     dto.Wind = new WindDto()
     {
         Direction = ParserHelpers.GetValue(xml.wind_dir_degreesSpecified, xml.wind_dir_degrees),
         Gust      = ParserHelpers.GetValue(xml.wind_gust_ktSpecified, xml.wind_gust_kt),
         Speed     = ParserHelpers.GetValue(xml.wind_speed_ktSpecified, xml.wind_speed_kt)
     };
     dto.FlightCategory = FlightCategoryType.ByName(xml.flight_category);
     dto.ObsType        = METARType.ByName(xml.metar_type);
     if (xml.maxT_cSpecified || xml.minT_cSpecified)
     {
         dto.TemperatureRange = new TemperatureRangeDto()
         {
             MaxTemperature = ParserHelpers.GetValue(xml.maxT_cSpecified, xml.maxT_c),
             MinTemperature = ParserHelpers.GetValue(xml.minT_cSpecified, xml.minT_c)
         };
     }
 }
        private ForecastDto GetForecast(string line, List <TAFCSVField> fieldOrder)
        {
            var dto = new ForecastDto()
            {
                GeographicData = new GeographicDataDto(),
                TAF            = new List <TAFDto>()
                {
                    new TAFDto()
                }
            };

            var        taf     = dto.TAF[0];
            TAFLineDto tafLine = new TAFLineDto();
            var        fields  = line.Split(ParserConstants.CSVSplitCharacter);

            for (var idx = 0; idx < fieldOrder.Count && idx < fields.Length; idx++)
            {
                try
                {
                    if (fieldOrder[idx] == TAFCSVField.fcst_time_from &&
                        String.IsNullOrWhiteSpace(fields[idx]))
                    {
                        // We've read all fields associated with this forecast so bail out early
                        break;
                    }

                    // Skip all unknown and empty fields
                    if (fieldOrder[idx] == TAFCSVField.Unknown ||
                        String.IsNullOrWhiteSpace(fields[idx]))
                    {
                        continue;
                    }

                    var fieldVal = fields[idx].Trim();

                    if (fieldOrder[idx] == TAFCSVField.raw_text)
                    {
                        taf.RawTAF = fieldVal;
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.altim_in_hg)
                    {
                        tafLine.Altimeter = ParserHelpers.ParseFloat(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.bulletin_time)
                    {
                        taf.BulletinTime = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.change_indicator)
                    {
                        tafLine.ChangeIndicator = ChangeIndicatorType.GetByName(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.cloud_base_ft_agl)
                    {
                        tafLine.SkyCondition[tafLine.SkyCondition.Count - 1].CloudBase = ParserHelpers.ParseInt(fieldVal) ?? DefaultValue.Height;
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.sky_cover)
                    {
                        tafLine.SkyCondition.Add(new SkyConditionDto()
                        {
                            SkyCondition = SkyConditionType.ByName(fieldVal)
                        });
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.fcst_time_from)
                    {
                        // This should be made safe, we are assuming all new DateTime objects
                        // have the same date
                        if (tafLine.ForecastTimeStart > ParserConstants.DefaultDateTime)
                        {
                            taf.TAFLine.Add(tafLine);
                            tafLine = new TAFLineDto();
                        }
                        tafLine.ForecastTimeStart = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.fcst_time_to)
                    {
                        tafLine.ForecastTimeEnd = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.icing_intensity)
                    {
                        var intensity = ParserHelpers.ParseInt(fieldVal) ?? -1;

                        tafLine.IcingHazards.Add(new HazardDto()
                        {
                            Intensity = IcingIntensity.ByValue(intensity)
                        });

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.icing_max_alt_ft_agl)
                    {
                        tafLine.IcingHazards[tafLine.IcingHazards.Count - 1].MaxAltitude =
                            ParserHelpers.ParseInt(fieldVal) ?? DefaultValue.Height;

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.icing_min_alt_ft_agl)
                    {
                        tafLine.IcingHazards[tafLine.IcingHazards.Count - 1].MinAltitude =
                            ParserHelpers.ParseInt(fieldVal) ?? DefaultValue.Height;

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.issue_time)
                    {
                        taf.IssuedTime = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.max_or_min_temp_c)
                    {
                        var tempFloat = ParserHelpers.ParseFloat(fieldVal);
                        if (tempFloat.HasValue)
                        {
                            if (tafLine.TemperatureRange == null)
                            {
                                tafLine.TemperatureRange = new TemperatureRangeDto()
                                {
                                    MaxTemperature = tempFloat
                                };
                            }
                            else
                            {
                                if (tempFloat > tafLine.TemperatureRange.MaxTemperature)
                                {
                                    tafLine.TemperatureRange.MinTemperature = tafLine.TemperatureRange.MaxTemperature;
                                    tafLine.TemperatureRange.MaxTemperature = tempFloat;
                                }
                                else
                                {
                                    tafLine.TemperatureRange.MinTemperature = tempFloat;
                                }
                            }
                        }
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.not_decoded)
                    {
                        tafLine.NotDecoded = fieldVal;
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.probability)
                    {
                        tafLine.Probability = ParserHelpers.ParseInt(fieldVal);

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.remarks)
                    {
                        taf.Remarks = fieldVal;
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.sfc_temp_c)
                    {
                        tafLine.Temperature = ParserHelpers.ParseFloat(fieldVal);

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.station_id)
                    {
                        dto.ICAO = fieldVal;
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.time_becoming)
                    {
                        tafLine.TimeBecoming = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.turbulence_intensity)
                    {
                        var intensity = ParserHelpers.ParseInt(fieldVal) ?? -1;

                        tafLine.TurbulenceHazards.Add(new HazardDto()
                        {
                            Intensity = TurbulenceIntensity.ByValue(intensity)
                        });

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.turbulence_max_alt_ft_agl)
                    {
                        tafLine.TurbulenceHazards[tafLine.TurbulenceHazards.Count - 1].MaxAltitude =
                            ParserHelpers.ParseInt(fieldVal) ?? DefaultValue.Height;

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.turbulence_min_alt_ft_agl)
                    {
                        tafLine.TurbulenceHazards[tafLine.TurbulenceHazards.Count - 1].MinAltitude =
                            ParserHelpers.ParseInt(fieldVal) ?? DefaultValue.Height;

                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.valid_time)
                    {
                        ///TODO: Temperature valid time, need to determine how this should be handled
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.valid_time_from)
                    {
                        taf.ValidTimeStart = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.valid_time_to)
                    {
                        taf.ValidTimeEnd = ParserHelpers.ParseDateTime(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.vert_vis_ft)
                    {
                        tafLine.VerticalVisibility = ParserHelpers.ParseInt(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.visibility_statute_mi)
                    {
                        tafLine.Visibility = ParserHelpers.ParseFloat(fieldVal);
                        continue;
                    }

                    if (fieldOrder[idx] == TAFCSVField.elevation_m)
                    {
                        dto.GeographicData.Elevation = ParserHelpers.ParseFloat(fieldVal);
                        continue;
                    }

                    if (fieldOrder[idx] == TAFCSVField.latitude)
                    {
                        dto.GeographicData.Latitude = ParserHelpers.ParseFloat(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.longitude)
                    {
                        dto.GeographicData.Longitude = ParserHelpers.ParseFloat(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wind_dir_degrees)
                    {
                        tafLine.Wind = new WindDto()
                        {
                            Direction = ParserHelpers.ParseInt(fieldVal)
                        };
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wind_gust_kt)
                    {
                        tafLine.Wind.Gust = ParserHelpers.ParseInt(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wind_speed_kt)
                    {
                        tafLine.Wind.Speed = ParserHelpers.ParseInt(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wx_string)
                    {
                        tafLine.Weather = fieldVal;
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wind_shear_hgt_ft_agl)
                    {
                        tafLine.WindShear = new WindShearDto()
                        {
                            Height = ParserHelpers.ParseInt(fieldVal)
                        };
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wind_shear_dir_degrees)
                    {
                        tafLine.WindShear.Direction = ParserHelpers.ParseInt(fieldVal);
                        continue;
                    }
                    if (fieldOrder[idx] == TAFCSVField.wind_shear_speed_kt)
                    {
                        tafLine.WindShear.Speed = ParserHelpers.ParseInt(fieldVal);
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Index: {idx}, Message: {ex.Message}");
                }
            }

            // Add the most recently parsed line if it has valid data
            if (tafLine.ForecastTimeStart > ParserConstants.DefaultDateTime)
            {
                dto.TAF[0].TAFLine.Add(tafLine);
            }

            return(dto);
        }
        private ObservationDto GetObservation(string line, List <METARCSVField> fieldOrder)
        {
            if (String.IsNullOrWhiteSpace(line))
            {
                throw new ArgumentException($"'{nameof(line)}' cannot be null or empty.");
            }

            if (fieldOrder == null ||
                fieldOrder.Count == 0)
            {
                throw new ArgumentException($"'{nameof(line)}' cannot be null or empty");
            }

            var dto = new ObservationDto()
            {
                GeographicData = new GeographicDataDto(),
                METAR          = new List <METARDto>()
                {
                    new METARDto()
                    {
                        ThreeHourObsData   = new ThreeHourObsData(),
                        SixHourData        = new SixHourObsDataDto(),
                        TwentyFourHourData = new TwentyFourHourObsDataDto(),
                        TemperatureRange   = new TemperatureRangeDto(),
                        Wind = new WindDto()
                    }
                }
            };
            var obs    = dto.METAR[0];
            var fields = line.Split(ParserConstants.CSVSplitCharacter);

            for (var idx = 0; idx < fieldOrder.Count && idx < fields.Count(); idx++)
            {
                // Skip all unknown and empty fields
                if (fieldOrder[idx] == METARCSVField.Unknown ||
                    String.IsNullOrWhiteSpace(fields[idx]))
                {
                    continue;
                }

                var fieldVal = fields[idx].Trim();

                if (fieldOrder[idx] == METARCSVField.altim_in_hg)
                {
                    obs.Altimeter = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.auto)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.Auto);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.auto_station)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.AutoStation);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.cloud_base_ft_agl)
                {
                    obs.SkyCondition[obs.SkyCondition.Count() - 1].CloudBase =
                        ParserHelpers.ParseInt(fieldVal) ?? DefaultValue.Height;
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.sky_cover)
                {
                    obs.SkyCondition.Add(new SkyConditionDto()
                    {
                        SkyCondition = SkyConditionType.ByName(fieldVal)
                    });
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.dewpoint_c)
                {
                    obs.Dewpoint = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.elevation_m)
                {
                    dto.GeographicData.Elevation = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.flight_category)
                {
                    obs.FlightCategory = FlightCategoryType.ByName(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.freezing_rain_sensor_off)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.FreezingRainSensorOff);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.latitude)
                {
                    dto.GeographicData.Latitude = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.longitude)
                {
                    dto.GeographicData.Longitude = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.lightning_sensor_off)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.LightningSensorOff);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.maintenance_indicator_on)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.MaintenanceIndicator);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.maxT24hr_c)
                {
                    obs.TwentyFourHourData.MaxTemperature = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.maxT_c)
                {
                    obs.TemperatureRange.MaxTemperature = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.metar_type)
                {
                    obs.ObsType = METARType.ByName(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.minT24hr_c)
                {
                    obs.TwentyFourHourData.MinTemperature = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.minT_c)
                {
                    obs.TemperatureRange.MinTemperature = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.no_signal)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.NoSignal);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.observation_time)
                {
                    obs.ObsTime = ParserHelpers.ParseDateTime(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.pcp24hr_in)
                {
                    obs.TwentyFourHourData.Precipitation = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.pcp3hr_in)
                {
                    obs.ThreeHourObsData.Precipitation = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.pcp6hr_in)
                {
                    obs.SixHourData.Precipitation = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.precip_in)
                {
                    obs.Precipitation = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.present_weather_sensor_off)
                {
                    if (IsFlagEnabled(fieldVal))
                    {
                        obs.QualityControlFlags.Add(QualityControlFlagType.PresentWeatherSensorOff);
                    }
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.raw_text)
                {
                    obs.RawMETAR = fieldVal;
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.sea_level_pressure_mb)
                {
                    obs.SeaLevelPressure = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.snow_in)
                {
                    obs.Snow = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.station_id)
                {
                    dto.ICAO = fieldVal;
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.temp_c)
                {
                    obs.Temperature = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.three_hr_pressure_tendency_mb)
                {
                    obs.ThreeHourObsData.PressureTendency = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.vert_vis_ft)
                {
                    obs.VerticalVisibility = ParserHelpers.ParseInt(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.visibility_statute_mi)
                {
                    obs.Visibility = ParserHelpers.ParseFloat(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.wind_dir_degrees)
                {
                    obs.Wind.Direction = ParserHelpers.ParseInt(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.wind_gust_kt)
                {
                    obs.Wind.Gust = ParserHelpers.ParseInt(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.wind_speed_kt)
                {
                    obs.Wind.Speed = ParserHelpers.ParseInt(fieldVal);
                    continue;
                }
                if (fieldOrder[idx] == METARCSVField.wx_string)
                {
                    obs.Weather = fieldVal;
                    continue;
                }
            }

            ResetFieldsToNull(obs);

            return(dto);
        }