using Serilog; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using TINK.Model.Bike; using TINK.Repository.Exception; using TINK.Repository.Response; using TINK.Model.Services.CopriApi.ServerUris; using TINK.Model.State; namespace TINK.Model.Connector { /// /// Conversion helper functionality. /// public static class TextToTypeHelper { /// Holds the text for demo bikes. private const string DEMOBIKEMARKER = "DEMO"; /// Part text denoting two wheel cargo bike.. private const string TWOWHEELCARGOMARKERFRAGMENT = "LONG"; /// /// Gets the position from StationInfo object. /// /// Object to get information from. /// Position information. public static IPosition GetPosition(this StationsAvailableResponse.StationInfo stationInfo) => GetPosition(stationInfo.gps); /// Gets the position from StationInfo object. /// Object to get information from. /// Position information. public static IEnumerable GetGroup(this AuthorizationResponse authorizationResponse) { try { return authorizationResponse.user_group.GetGroup(); } catch (Exception l_oException) { throw new Exception($"Can not get group of user from text \"{authorizationResponse.user_group}\".", l_oException); } } /// Gets the position from StationInfo object. /// Object to get information from. /// Position information. public static IEnumerable GetGroup(this string[] group) { if (group == null || group.Length == 0) { // If not logged in stations groups are empty form COPRI version v4.1. Log.Debug("Can not get goup form string. Group text can not be null."); return new List(); } return new HashSet(group).ToList(); } /// Gets if user acknowldged ags or not. /// Object to get information from. /// Position information. public static bool GetIsAgbAcknowledged(this AuthorizationResponse authorizationResponse) => int.TryParse(authorizationResponse?.agb_checked, out int result) && result != 0; /// Gets the position from StationInfo object. /// Object to get information from. /// Position information. public static string GetGroup(this IEnumerable group) { return string.Join(",", group); } /// Gets the position from StationInfo object. /// Object to get information from. /// Position information. public static IEnumerable GetGroup(this StationsAvailableResponse.StationInfo stationInfo) { try { return stationInfo.station_group.GetGroup(); } catch (Exception l_oException) { throw new Exception($"Can not get group of stations from text \"{stationInfo.station_group}\".", l_oException); } } /// /// Gets the position from StationInfo object. /// /// Object to get information from. /// Position information. public static InUseStateEnum GetState(this BikeInfoBase bikeInfo) { var l_oState = bikeInfo.state; if (string.IsNullOrEmpty(l_oState)) { throw new InvalidResponseException( string.Format("Unknown reservation state detected. Member {0}.{1}.", typeof(BikeInfoBase), nameof(BikeInfoBase.state)), bikeInfo); } if (l_oState == "available") { return InUseStateEnum.Disposable; } else if (l_oState == "reserved" || l_oState == "requested") { return InUseStateEnum.Reserved; } else if (l_oState == "booked" || l_oState == "occupied") { return InUseStateEnum.Booked; } throw new CommunicationException(string.Format("Unknown bike state detected. State is {0}.", l_oState)); } /// /// Gets the from date information from JSON. /// /// JSON to get information from.. /// From information. public static DateTime GetFrom(this BikeInfoReservedOrBooked bikeInfo) { return DateTime.Parse(bikeInfo.start_time); } /// /// Gets whether the bike is a trike or not. /// /// JSON to get information from.. /// From information. public static bool? GetIsDemo(this BikeInfoBase bikeInfo) { return bikeInfo?.description != null ? bikeInfo.description.ToUpper().Contains(DEMOBIKEMARKER) : (bool?) null; } /// /// Gets whether the bike is a trike or not. /// /// JSON to get information from. /// From information. public static IEnumerable GetGroup(this BikeInfoBase bikeInfo) { try { return bikeInfo?.bike_group?.GetGroup()?.ToList() ?? new List(); } catch (Exception l_oException) { throw new Exception($"Can not get group of user from text \"{bikeInfo.bike_group}\".", l_oException); } } /// Gets whether the bike has a bord computer or not. /// JSON to get information from. /// From information. public static bool GetIsManualLockBike(this BikeInfoBase bikeInfo) { return !string.IsNullOrEmpty(bikeInfo.system) && bikeInfo.system.ToUpper().StartsWith("LOCK"); } /// Gets whether the bike has a bord computer or not. /// JSON to get information from.. /// From information. public static bool GetIsBluetoothLockBike(this BikeInfoBase bikeInfo) { return !string.IsNullOrEmpty(bikeInfo.system) && bikeInfo.system.ToUpper().StartsWith("ILOCKIT"); } /// Gets whether the bike has a bord computer or not. /// JSON to get information from.. /// From information. public static int GetBluetoothLockId(this BikeInfoAvailable bikeInfo) { return TextToLockItTypeHelper.GetBluetoothLockId(bikeInfo?.Ilockit_ID); } /// Gets whether the bike has a bord computer or not. /// JSON to get information from.. /// From information. public static Guid GetBluetoothLockGuid(this BikeInfoAvailable bikeInfo) { // return new Guid("00000000-0000-0000-0000-e57e6b9aee16"); return Guid.TryParse(bikeInfo?.Ilockit_GUID, out Guid lockGuid) ? lockGuid : TextToLockItTypeHelper.INVALIDLOCKGUID; } public static byte[] GetUserKey(this BikeInfoReservedOrBooked bikeInfo) { return GetKey(bikeInfo.K_u); } public static byte[] GetAdminKey(this BikeInfoReservedOrBooked bikeInfo) { return GetKey(bikeInfo.K_a); } public static byte[] GetSeed(this BikeInfoReservedOrBooked bikeInfo) { return GetKey(bikeInfo.K_seed); } /// /// Get array of keys from string of format "[12, -9, 5]" /// /// /// private static byte[] GetKey(string keyArrayText) { try { if (string.IsNullOrEmpty(keyArrayText)) return new byte[0]; return Regex.Replace(keyArrayText, @"\[(.*)\]", "$1").Split(',').Select(x => (byte)sbyte.Parse(x)).ToArray(); } catch (Exception exception) { Log.Error("Can not extract K_u/ K_a/ or K_seed. Key {ArrayText} does not is not of expected format. {Exception}", keyArrayText, exception); return new byte[0]; } } /// /// Gets whether the bike is a trike or not. /// /// JSON to get information from.. /// From information. public static WheelType? GetWheelType(this BikeInfoBase bikeInfo) { var l_oDescription = bikeInfo.description; if (l_oDescription == null) { // Can not get type of wheel if description text is empty. return null; } foreach (WheelType l_oWheelType in Enum.GetValues(typeof(WheelType))) { if (l_oDescription.ToUpper().Contains(l_oWheelType.ToString().ToUpper())) { return l_oWheelType; } } // Check for custom value "Long". if (l_oDescription.ToUpper().Contains(TWOWHEELCARGOMARKERFRAGMENT)) { return WheelType.Two; } // Check for Stadrad. if (GetTypeOfBike(bikeInfo) == TypeOfBike.Citybike) { return WheelType.Two; } return null; } /// /// Gets the type of bike. /// /// Object to get bike type from. /// Type of bike. public static TypeOfBike? GetTypeOfBike(this BikeInfoBase bikeInfo) { var l_oDescription = bikeInfo?.description; if (l_oDescription == null) { // Can not get type of wheel if description text is empty. return null; } foreach (TypeOfBike l_oTypeOfBike in Enum.GetValues(typeof(TypeOfBike))) { if (l_oDescription.ToUpper().Contains(l_oTypeOfBike.GetCopriText().ToUpper())) { return l_oTypeOfBike; } } return null; } /// Get position from a ,- separated string. /// Text to extract positon from. /// Position object. public static IPosition GetPosition(Repository.Response.Position gps) => PositionFactory.Create( double.TryParse(gps?.latitude, NumberStyles.Float, CultureInfo.InvariantCulture, out double latitude) ? latitude : double.NaN, double.TryParse(gps?.longitude, NumberStyles.Float, CultureInfo.InvariantCulture, out double longitude) ? longitude : double.NaN); /// Get position from a ,- separated string. /// Text to extract positon from. /// Position object. public static Map.IMapSpan GetMapSpan(this MapSpan mapSpan) => Map.MapSpanFactory.Create( GetPosition(mapSpan?.center), double.TryParse(mapSpan?.radius, NumberStyles.Float, CultureInfo.InvariantCulture, out double radius) ? radius: double.NaN); /// Gets text of bike from. /// Type to get text for. /// public static string GetCopriText(this TypeOfBike p_eType) { switch (p_eType) { case TypeOfBike.Citybike: return "Stadtrad"; default: return p_eType.ToString(); } } public static Uri GetOperatorUri(this BikeInfoBase bikeInfo) { return bikeInfo?.uri_operator != null && !string.IsNullOrEmpty(bikeInfo?.uri_operator) ? new Uri($"{bikeInfo.uri_operator}/{CopriServerUriList.REST_RESOURCE_ROOT}") : null; } /// Tries to get the copriversion from response. /// Response to get version info from. /// COPRI version public static bool TryGetCopriVersion(this CopriVersion response, out Version copriVersion) { copriVersion = new Version(0, 0); return response != null && !string.IsNullOrEmpty(response.copri_version) && Version.TryParse(response.copri_version, out copriVersion); } /// Gets the copriversion from. /// Response to get version info from. /// COPRI version public static Version GetCopriVersion(this CopriVersion response) => response.TryGetCopriVersion(out Version copriVersion) ? copriVersion : throw new InvalidResponseException($"Can not get version info from copri response {response?.copri_version}."); } }