private void DrawBgmTooltip(BgmInfo bgm)
 {
     ImGui.BeginTooltip();
     ImGui.PushTextWrapPos(GuiScale(400f));
     ImGui.TextColored(new Vector4(0, 1, 0, 1), "Song Info");
     ImGui.TextWrapped($"Title: {bgm.Title}");
     ImGui.TextWrapped(string.IsNullOrEmpty(bgm.Location) ? "Location: Unknown" : $"Location: {bgm.Location}");
     if (!string.IsNullOrEmpty(bgm.AdditionalInfo))
     {
         ImGui.TextWrapped($"Info: {bgm.AdditionalInfo}");
     }
     ImGui.Text($"File path: {bgm.FilePath}");
     ImGui.PopTextWrapPos();
     ImGui.EndTooltip();
 }
        private void UpdateBgmDescription(ushort currentBgmId, BgmInfo currentBgm)
        {
            string songDescription = $"({currentBgmId}) {currentBgm.Title}";
            string truncateFormat  = "{0} ... (?)";

            if (ImGui.CalcTextSize(songDescription).X < 280f)
            {
                _songDescription = songDescription;
            }
            else
            {
                string tmpDesc;
                int    substring = songDescription.Length;
                do
                {
                    tmpDesc = string.Format(truncateFormat, songDescription.Substring(0, substring--));
                } while (ImGui.CalcTextSize(tmpDesc).X > 280f);
                _songDescription = tmpDesc;
            }
        }
        private void DrawCreation()
        {
            if (!ImGui.BeginTabItem("Create"))
            {
                return;
            }

            ImGui.Text("This tab allows you to create a custom title screen.");
            ImGui.Text("It is recommended to use first-person view.");
#if DEBUG
            ImGui.BeginChild("scrolling", new Vector2(0, GuiScale(400)), true, ImGuiWindowFlags.NoScrollbar);
#else
            ImGui.BeginChild("scrolling", new Vector2(0, GuiScale(400)), true, ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoScrollbar);
#endif
            bool    stateInvalid;
            BgmInfo selectedBgm = new BgmInfo {
                Title = "Unknown", FilePath = ""
            };
            Vector3 eyesPos = default;
            Vector3 lookAt  = default;
#if !DEBUG
            if (_clientState.LocalPlayer?.Position == null)
            {
                ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "The current game state is invalid for creating a title screen.");
                stateInvalid = true;
                _nameEmpty   = false; //hax
            }
            else
#endif
            {
                ImGui.Text("Name of custom title screen:");
                ImGui.SameLine();
                ImGui.SetNextItemWidth(GuiScale(280f));
                if (ImGui.InputText("##title_screen_name", ref _customTsName, 64))
                {
                    _nameEmpty = false;
                    _nameContainsInvalidCharacters = false;
                    _nameAlreadyExists             = false;

                    if (string.IsNullOrEmpty(_customTsName))
                    {
                        _nameEmpty = true;
                    }
                    else if (_customTsName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || _customTsName.StartsWith("TE_"))
                    {
                        _nameContainsInvalidCharacters = true;
                    }
                    else
                    {
                        _titleScreenSavePath = Path.Combine(_titleScreenFolder, _customTsName + ".json");
                        if (File.Exists(_titleScreenSavePath) || _customTsName == "Random" || _customTsName == "Random (custom)")
                        {
                            _nameAlreadyExists = true;
                        }
                    }
                }

                stateInvalid = _nameAlreadyExists | _nameContainsInvalidCharacters | _nameEmpty;

                ImGui.Text("Logo setting:");
                ImGui.SameLine();
                if (_titleLogosCreate[_selectedLogoIndexCreate] != "Unspecified")
                {
                    ImGui.SetNextItemWidth(GuiScale(263f));
                }
                else
                {
                    ImGui.SetNextItemWidth(GuiScale(368f));
                }
                ImGui.Combo("##titleeditLogoSetting", ref _selectedLogoIndexCreate, _titleLogosCreate, _titleLogosCreate.Length);
                if (_titleLogosCreate[_selectedLogoIndexCreate] != "Unspecified")
                {
                    ImGui.SameLine();
                    ImGui.Checkbox("Display logo", ref _selectedLogoVisibleCreate);
                }
#if DEBUG
                ImGui.Text("Terri path:");
                ImGui.SameLine();
                ImGui.SetNextItemWidth(GuiScale(220f));
                ImGui.InputText("##manualTerritype", ref _terriPath, 64);
                var search  = _territoryPaths.Values.Where(row => row.Bg == _terriPath);
                var results = search as TerritoryType[] ?? search.ToArray();
                if (results.Any())
                {
                    var val = results[0];
                    ImGui.SameLine();
                    ImGui.PushTextWrapPos(GuiScale(450f));
                    ImGui.TextWrapped($"{val.PlaceName.Value.Name}");
                    ImGui.PopTextWrapPos();
                }
#else
                if (!_territoryPaths.ContainsKey(_clientState.TerritoryType))
                {
                    ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "The current territory is not valid for a title screen.");
                    stateInvalid = true;
                }
                else
                {
                    ImGui.Text($"Title screen zone: {_territoryPaths[_clientState.TerritoryType].PlaceName.Value.Name}");
                }
#endif

                eyesPos = EyesPos(_clientState.LocalPlayer?.Position ?? new Vector3 {
                    X = 0, Y = 0, Z = 0
                });
                ImGui.Text($"Camera position: {eyesPos.X:F2}, {eyesPos.Y:F2}, {eyesPos.Z:F2}");

                lookAt = LookAt(new Vector3(eyesPos.X, eyesPos.Y, eyesPos.Z));
                ImGui.Text($"\"Fix-on\" position: {lookAt.X:F2}, {lookAt.Y:F2}, {lookAt.Z:F2}");

                ImGui.Text("Title camera FOV (in-game FoV will not change)");
                ImGui.SameLine();
                ImGui.SetNextItemWidth(GuiScale(128f));
                ImGui.SliderFloat("##customTsFovY", ref _fovY, 0.01f, 3f, "%.2f");
                ImGui.SameLine();
                if (ImGui.Button("Reset##resetFovY"))
                {
                    _fovY = 1f;
                }

                // Weather
                var newType = _clientState.TerritoryType;
                if (newType != _lastTerritoryId)
                {
                    _lastTerritoryId = newType;
                    UpdateWeathers(newType);
                }

                byte currentWeather = _titleEdit.GetWeather();
                ImGui.Text($"Current weather: {GetWeatherDescriptor(currentWeather)}");
                ImGui.SameLine();
                ImGui.SetCursorPosX(GuiScale(383f));
                if (ImGui.Button("Set Current##weather"))
                {
                    _inputIntWeatherId = currentWeather;
                    _weatherId         = currentWeather;
                    if (_territoryWeathers != null)
                    {
                        for (int i = 0; i < _territoryWeathers.Length; i++)
                        {
                            if (_territoryWeathers[i] != _weatherId)
                            {
                                continue;
                            }
                            _selectedWeatherIndex = i;
                            break;
                        }
                    }
                }
                ImGui.Text("Title zone weather:");
                ImGui.SameLine();
#if !DEBUG
                if (_territoryWeathers == null || _territoryWeathers.Length == 0)
#endif
                {
                    ImGui.SetNextItemWidth(GuiScale(90));
                    if (ImGui.InputInt("##customTSweather", ref _inputIntWeatherId))
                    {
                        _weatherId = _inputIntWeatherId;
                    }
                }
#if !DEBUG
                else
                {
                    ImGui.SetNextItemWidth(GuiScale(332f));
                    if (ImGui.BeginCombo("##customTSweatherCombo", GetWeatherDescriptor(_territoryWeathers[_selectedWeatherIndex])))
                    {
                        for (int i = 0; i < _territoryWeathers.Length; i++)
                        {
                            if (ImGui.Selectable(GetWeatherDescriptor(_territoryWeathers[i])))
                            {
                                _selectedWeatherIndex = i;
                                _weatherId            = _territoryWeathers[_selectedWeatherIndex];
                            }
                        }
                        ImGui.EndCombo();
                    }
                }
#endif

                if (_weatherId < 1 || _weatherId > 255 || !_weathers.ContainsKey((uint)_inputIntWeatherId))
                {
                    ImGui.SameLine();
                    ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "(invalid)");
                    stateInvalid = true;
                }
                else if (_territoryWeathers == null || _territoryWeathers.Length == 0)
                {
                    ImGui.SameLine();
                    ImGui.Text(GetWeatherDescriptor((ushort)_weatherId));
                }

                // Music
                ushort currentBgmId = _titleEdit.GetSong();
                var    currentBgm   = _bgmSheet.GetBgmInfo(currentBgmId);
                if (_lastBgmId != currentBgmId)
                {
                    _lastBgmId = currentBgmId;
                    UpdateBgmDescription(currentBgmId, currentBgm);
                }
                ImGui.Text("Current song:");
                ImGui.SameLine();
                ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0.5f, 0.5f, 0.5f, 1));
                ImGui.Text(_songDescription);
                ImGui.PopStyleColor();
                if (ImGui.IsItemHovered())
                {
                    DrawBgmTooltip(currentBgm);
                }
                ImGui.SameLine();
                ImGui.SetCursorPosX(GuiScale(383f));
                if (ImGui.Button("Set Current##music"))
                {
                    _selectedBgmId = currentBgmId;
                }
                ImGui.Text("Title zone music:");
                ImGui.SameLine();
                ImGui.SetNextItemWidth(GuiScale(150f));
                ImGui.InputInt("##customTSmusic", ref _selectedBgmId);
                selectedBgm = _bgmSheet.GetBgmInfo((ushort)_selectedBgmId);
                if (ImGui.IsItemHovered())
                {
                    DrawBgmTooltip(selectedBgm);
                }
                ImGui.SameLine();
                if (_selectedBgmId < 1 || currentBgm.Title == "Invalid")
                {
                    ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "(invalid)");
                    stateInvalid = true;
                }
                else
                {
                    ImGui.TextWrapped($"{selectedBgm.Title}");
                }

                // Time
                long etS = 0;
                unsafe
                {
                    etS = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()->EorzeaTime;
                }
                var et = DateTimeOffset.FromUnixTimeSeconds(etS);
                ImGui.Text($"Current time: {et.Hour:D2}:{et.Minute:D2}");
                ImGui.SameLine();
                ImGui.SetCursorPosX(GuiScale(383f));
                if (ImGui.Button("Set Current##time"))
                {
                    _tsTimeHrs = et.Hour;
                    _tsTimeMin = et.Minute;
                    var scaled = _tsTimeMin / 60f * 100;
                    _tsTimeOffset = (int)(_tsTimeHrs * 100 + scaled % 100);
                }
                ImGui.Text("Time:");
                ImGui.SameLine();
                float timeWidth = GuiScale(50f);
                ImGui.SetNextItemWidth(timeWidth);
                if (ImGui.InputInt("hrs", ref _tsTimeHrs, 0, 23))
                {
                    var scaled = _tsTimeMin / 60f * 100;
                    _tsTimeOffset = (int)(_tsTimeHrs * 100 + scaled % 100);
                }
                ImGui.SameLine();
                ImGui.SetNextItemWidth(timeWidth);
                if (ImGui.InputInt("mins", ref _tsTimeMin, 0, 59))
                {
                    var scaled = _tsTimeMin / 60f * 100;
                    _tsTimeOffset = (int)(_tsTimeHrs * 100 + scaled % 100);
                }
            }

#if DEBUG
            ImGui.Text($"_stateInvalid: {stateInvalid}");
            ImGui.Text($"_nameEmpty: {_nameEmpty}");
            ImGui.Text($"_fileWasCreatedRecently: {_fileWasCreatedRecently}");
            ImGui.Text($"_nameAlreadyExists: {_nameAlreadyExists}");
            ImGui.Text($"_nameContainsInvalidCharacters: {_nameContainsInvalidCharacters}");
#endif
            ImGui.EndChild();

            if (_fileWasCreatedRecently)
            {
                ImGui.TextColored(new Vector4(0f, 1f, 0f, 1f), "Title screen created!");
                ImGui.EndTabItem();
                return;
            }

            if (_nameEmpty)
            {
                ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "The title screen name is empty.");
            }

            if (_nameAlreadyExists)
            {
                ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "A custom title screen already exists by this name.");
            }
            else if (_nameContainsInvalidCharacters)
            {
                ImGui.TextColored(new Vector4(1f, 0f, 0f, 1f), "Title must be a valid filename without extension.");
            }

            if (stateInvalid)
            {
                ImGui.EndTabItem();
                return;
            }

            if (ImGui.Button("Generate"))
            {
                TitleEditScreen scr = new TitleEditScreen();
                scr.Name        = _customTsName;
                scr.Logo        = _titleLogosCreate[_selectedLogoIndexCreate];
                scr.DisplayLogo = _selectedLogoVisibleCreate;
#if DEBUG
                scr.TerritoryPath = _terriPath;
#else
                scr.TerritoryPath = _territoryPaths[_clientState.TerritoryType].Bg.ToString();
#endif
                scr.CameraPos  = eyesPos;
                scr.FixOnPos   = lookAt;
                scr.FovY       = _fovY;
                scr.WeatherId  = (byte)_weatherId;
                scr.TimeOffset = (ushort)_tsTimeOffset;
                scr.BgmPath    = selectedBgm.FilePath;
                var  text          = JsonConvert.SerializeObject(scr, Formatting.Indented);
                bool createSuccess = false;
                try
                {
                    File.WriteAllText(_titleScreenSavePath, text);
                    EnumerateTitleScreenFiles();
                    createSuccess = true;
                }
                catch (Exception e)
                {
                    PluginLog.LogError(e, "Error occurred saving title screen.");
                }

                if (createSuccess)
                {
                    _nameAlreadyExists      = true;
                    _fileWasCreatedRecently = true;
                    Task.Delay(2000).ContinueWith(_ => _fileWasCreatedRecently = false);
                }
            }

            ImGui.EndTabItem();
        }