using System.Globalization; using System.Numerics; using System.Text; using ShippingCosts.Contracts; using ShippingCosts.Implementations; using Spectre.Console; Console.OutputEncoding = Encoding.UTF8; ICountryDistanceProvider countryDistanceProvider = new CountryDistanceProvider(); var shippingServices = new IShippingCostCalculator[] { new SelfPickup(), new LPSShippingCostCalculator(countryDistanceProvider), new LuxuryShippingCostCalculator(countryDistanceProvider) }; Clear(); WriteLine("Please enter the dimensions of the package you want to ship."); var box = AskForBoxMeasurements(); Clear(); var selectedService = AskForShippingService(); Clear(); string? targetCountry = AskForCountryName(countryDistanceProvider); Clear(); if (targetCountry is null) { WriteLine("Sorry to see you go! We hope you'll ship with us again soon."); return; } decimal cost = selectedService.CalculateShippingCosts(targetCountry, box)!.Value; WriteLine($"The shipping costs for your package are {cost.ToString("C", CultureInfo.CurrentCulture)}."); Console.ReadKey(); #region Helper methods static void WriteLine(string text) => AnsiConsole.MarkupLine($"[blue]{text}[/]"); static void Clear() { AnsiConsole.Clear(); AnsiConsole.WriteLine(); AnsiConsole.Write(new FigletText("Shipping Cost Calculator") .Color(Color.Blue) .Centered()); } static IMeasuredBox AskForBoxMeasurements() { int width = AskForPositiveValue("Enter the package width in mm:"); int height = AskForPositiveValue("Enter the package height in mm:"); int depth = AskForPositiveValue("Enter the package depth in mm:"); double weight = AskForPositiveValue("Enter the package weight in kg:"); return new Package { Depth = depth, Height = height, Weight = weight, Width = width }; static T AskForPositiveValue(string prompt) where T : INumber { T value; do { value = AnsiConsole.Ask(prompt); if (value <= T.Zero) { AnsiConsole.MarkupLine("[red]Invalid input. Please enter a value greater than 0.[/]"); } } while (value <= T.Zero); return value; } } IShippingCostCalculator AskForShippingService() { var services = new Dictionary(); foreach (var shippingService in shippingServices) { services.Add(shippingService.CarrierName, shippingService); } SelectionPrompt prompt = new SelectionPrompt() .Title("Please choose a shipping service/type:") .AddChoices(services.Keys); string selection = AnsiConsole.Prompt(prompt); return services[selection]; } static string? AskForCountryName(ICountryDistanceProvider countryDistanceProvider) { string countryName; const string Abort = "x"; do { countryName = AnsiConsole.Ask($"Enter the country name to ship to (or type '{Abort}' to abort):"); if (countryName.Trim().Equals(Abort, StringComparison.OrdinalIgnoreCase)) { AnsiConsole.MarkupLine("[yellow]Aborted by user.[/]"); return null; } countryName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(countryName.ToLower()); if (!countryDistanceProvider.IsPossibleCountry(countryName)) { AnsiConsole.MarkupLine("[red]Shipping to the provided country is not possible. " + $"Please try again or type '{Abort}' to cancel.[/]"); } } while (string.IsNullOrWhiteSpace(countryName) || !countryDistanceProvider.IsPossibleCountry(countryName)); return countryName; } #endregion