using System.Text; using Godot; using openF1Manager.scripts; namespace openF1Manager.scenes.Main; public partial class SessionTool : MarginContainer { private LineEdit _meetingKey; private LineEdit _sessionKey; private LineEdit _location; private LineEdit _dateStart; private LineEdit _dateEnd; private string _baseAddress = "https://api.openf1.org/v1/sessions?"; // MVVM-lite private SessionFilter _vm; private FormBinder _binder; public override void _Ready() { _meetingKey = GetNode( "ElementsVBoxContainer/PropertiesVBoxContainer/MeetingKeyHBoxContainer/MeetingKeyLineEdit"); _sessionKey = GetNode( "ElementsVBoxContainer/PropertiesVBoxContainer/SessionKeyHBoxContainer/SessionKeyLineEdit"); _location = GetNode( "ElementsVBoxContainer/PropertiesVBoxContainer/LocationHBoxContainer/LocationLineEdit"); _dateStart = GetNode("ElementsVBoxContainer/PropertiesVBoxContainer/DateStartHBoxContainer/DateStartLineEdit"); _dateEnd = GetNode( "ElementsVBoxContainer/PropertiesVBoxContainer/DateEndHBoxContainer/DateEndLineEdit"); GD.Print(_meetingKey.PlaceholderText); // Create VM and bind all LineEdits under the form root _vm = new SessionFilter(); var formRoot = GetNode("ElementsVBoxContainer/PropertiesVBoxContainer"); _binder = new FormBinder(_vm, this).Bind(); } public string BuildQueryOld() { GD.Print("Building query"); var queryString = System.Web.HttpUtility.ParseQueryString(string.Empty); foreach (var pair in new (string Key, string Value)[] { ("meeting_key", _meetingKey.Text), ("session_key", _sessionKey.Text), ("location", _location.Text), ("date_start", _dateStart.Text), ("date_end", _dateEnd.Text), }) { if (!string.IsNullOrWhiteSpace(pair.Value)) queryString[pair.Key] = pair.Value; } return _baseAddress + queryString.ToString(); } public string BuildQuery() { GD.Print("Building query"); return ApiModelSerializer.ToQueryString(_vm, _baseAddress); } public async void SendRequest() { string requestAddress = BuildQuery(); GD.Print(requestAddress); var httpRequest = GetParent().GetParent().GetNode("HTTPRequest"); httpRequest.RequestCompleted += OnSessionRequestCompleted; GD.Print("Sending request"); var error = httpRequest.Request(requestAddress); if (error != Error.Ok) { GD.Print(error); return; } } private void OnSessionRequestCompleted(long result, long responseCode, string[] headers, byte[] body) { GD.Print("Request completed"); var text = Encoding.UTF8.GetString(body); var parsed = Json.ParseString(text); if (parsed.VariantType != Variant.Type.Array) { GD.PrintErr("Unexpected JSON format. Expected array."); return; } var arr = parsed.AsGodotArray(); if (arr.Count == 0) { GD.Print("No sessions returned."); return; } // Populate List of sessions ItemList itemList = GetNode("ElementsVBoxContainer/ControlsVBoxContainer/ItemList"); itemList.Clear(); int count = 0; foreach (var item in arr) { count++; itemList.AddItem(count.ToString()); } // Populate UI fields from first item in array var first = arr[0]; if (first.VariantType != Variant.Type.Dictionary) { GD.PrintErr("Unexpected item format. Expected dictionary."); return; } var dict = first.AsGodotDictionary(); // Populate VM; binder reflects it to UI fields ApiModelSerializer.PopulateFromDictionary(_vm, dict); } private void PopulateFieldsFromDictionary(Godot.Collections.Dictionary dict) { // Helper to read a value with multiple possible key casings string GetString(params string[] keys) { foreach (var k in keys) { if (dict.ContainsKey(k) && dict[k].VariantType != Variant.Type.Nil) return dict[k].ToString(); } return string.Empty; } // Map likely API keys; try snake_case first, then camelCase fallbacks _meetingKey.Text = GetString("meeting_key", "meetingKey"); _sessionKey.Text = GetString("session_key", "sessionKey"); _location.Text = GetString("location", "Location"); _dateStart.Text = GetString("date_start", "dateStart", "DateStart"); _dateEnd.Text = GetString("date_end", "dateEnd", "DateEnd"); } }