Newer
Older
EARS-LINUX / program / MainWindow.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;
using System.Runtime.InteropServices;

public class MainWindow : Window
{

     // ---- stack + pages (new all-in-one glade) ----
    [UI] private Notebook RootNoteBook = null;

    private const int PageSplash   = 0;
    private const int PageSettings = 1;
    private const int PageSound = 2;
    private const int PageMap = 3;

    // ---- splash page widgets ----
    [UI] private Button Option1Button = null;
    [UI] private Button Option2Button = null;
    [UI] private Button Option3Button = null;
    // [UI] private Switch LanguageSwitch = null;
    [UI] private ToggleButton LanguageToggle = null;
    [UI] private Label  LanguageLabel = null;
    [UI] private Button CloseButton = null;
    [UI] private Button SettingButton = null;
    [UI] private Image  ConnectionIcon = null;   // was GtkIconView in the old glade

    private CssProvider _scaleCssProvider;
    // private CssProvider _languageCssProvider;
    private int _lastAppliedWidth = -1;

    private const int BaseWidth = 1920;
    private const int BaseHeight = 1080;

    // private const string EngCssPath = "lang_eng.css";
    // private const string JpnCssPath = "lang_jpn.css";

    // private Dictionary<string, string> _engText;
    // private Dictionary<string, string> _jpnText;
    // private Dictionary<string, Button> _buttonsById;
    private bool _connected;

    private readonly ArduinoConnection _arduino = new ArduinoConnection();
    internal SettingsWindow _settingsWindow;
    internal Language _Language;

    internal SoundWindow _soundWindow;
    internal MapWindow _mapWindow;

    public MainWindow() : this(CreateBuilder()) { }

    private static Builder CreateBuilder()
    {
        var builder = new Builder();
        builder.AddFromFile("homepageallv6.glade");
        return builder;
    }

    private MainWindow(Builder builder) : base(builder.GetObject("Root").Handle)
    {
        builder.Autoconnect(this);

        _scaleCssProvider = new CssProvider();
        // _languageCssProvider = new CssProvider();

        StyleContext.AddProviderForScreen(Gdk.Screen.Default, _scaleCssProvider, 600);
        // StyleContext.AddProviderForScreen(Gdk.Screen.Default, _Language._languageCssProvider, 600);

        // // make it full screen
        // this.Fullscreen();
        //use the linux computer resolution
        this.SetDefaultSize(1280, 800);
        this.SetPosition(WindowPosition.Center);

        // RootStack.TransitionType     = StackTransitionType.None;
        // RootStack.TransitionType     = StackTransitionType.SlideLeftRight;
        // RootStack.TransitionDuration = 150;

        Option1Button.Clicked += OnNormalSessionClicked;
        Option2Button.Clicked += OnPositionRecordingClicked;
        Option3Button.Clicked += OnExamModeClicked;
        CloseButton.Clicked   += OnCloseClicked;
        SettingButton.Clicked += OnSettingClicked;
        // LanguageSwitch.AddNotification("active", OnLanguageSwitchNotify);
        LanguageToggle.Toggled += OnLanguageToggled;

        DeleteEvent   += OnDeleteEvent;
        SizeAllocated += OnWindowSizeAllocated;

        _arduino.ConnectionChanged += OnArduinoConnectionChanged;


        // // _buttonsById = new Dictionary<string, Button>
        // // {
        // //     { "Option1Button", Option1Button },
        // //     { "Option2Button", Option2Button },
        // //     { "Option3Button", Option3Button },
        // //     { "CloseButton",   CloseButton   },
        // //     { "SettingButton", SettingButton },
        // // };

        // // CSS ID selectors (#Option1Button etc.) need the widget Name set explicitly
        // foreach (var kvp in _buttonsById)
        //     kvp.Value.Name = kvp.Key;

        // // _engText = LoadButtonText("info_eng.csv");
        // // _jpnText = LoadButtonText("info_jpn.csv");

        // _Language = new Language(_buttonsById,LanguageLabel);

        // // _Language.ApplyLanguage(LanguageSwitch.Active);

        _Language = new Language(LanguageToggle.Active);
        _Language.Register("Option1Button",  Option1Button);
        _Language.Register("Option2Button",  Option2Button);
        _Language.Register("Option3Button",  Option3Button);
        _Language.Register("CloseButton",    CloseButton);
        _Language.Register("SettingButton",  SettingButton);
        _Language.Register("LanguageLabel",  LanguageLabel);
        _Language.Register("LanguageToggle", LanguageToggle);
        _Language.RegisterDynamic("ConnectionIcon", ConnectionIcon);

        _settingsWindow = new SettingsWindow(builder, _arduino, _Language);
        _settingsWindow.BackRequested += OnSettingsClosed;

        _mapWindow = new MapWindow(builder, _Language);
        _mapWindow.BackRequested += OnMapClosed;

        _Language.Changed += (s, e) => OnArduinoConnectionChanged(_connected);
        _Language.Apply(LanguageToggle.Active);

        // // single source of truth: the toggle drives both pages
        // ApplyLanguageEverywhere(LanguageToggle.Active);

        RootNoteBook.ShowTabs    = false;
        RootNoteBook.ShowBorder  = false;
        RootNoteBook.CurrentPage = PageSplash;
        _soundWindow = new SoundWindow(builder);
        _soundWindow.BackRequested += (s, e) => RootNoteBook.CurrentPage = PageSplash;
        _soundWindow.CaseSelected  += OnCaseSelected;


        OnArduinoConnectionChanged(false);
    }

    // ---------- settings panel ----------

    private void OnSettingClicked(object sender, EventArgs e)
    {
        // if (_settingsWindow == null)
        // {
        //     // _settingsWindow = new SettingsWindow(_arduino);
        //     // _settingsWindow.BackRequested += OnSettingsClosed;
        //     // _settingsWindow.ShowAll();      // build it fullscreen once, up front
        //     // _settingsWindow.Fullscreen();
        //     RootNoteBook.CurrentPage = PageSettings;
        // }

        
        // _settingsWindow.ShowAll();
        // _settingsWindow.Present();
        // this.Hide();
        RootNoteBook.CurrentPage = PageSettings;
    }

    private void OnSettingsClosed(object sender, EventArgs e)
    {
        // this.ShowAll();
        // this.Present();
        // _settingsWindow.Hide(); 
        RootNoteBook.CurrentPage = PageSplash;
    }

    private void OnArduinoConnectionChanged(bool connected)
    {
        _connected = connected;
        if (ConnectionIcon == null) return;

        ConnectionIcon.SetFromIconName(
            connected ? "network-transmit-receive" : "network-offline",
            IconSize.Dnd);

        ConnectionIcon.TooltipText = connected
            ? _Language.Format("ConnectionIcon.Connected", _arduino.PortName)
            : _Language["ConnectionIcon.Disconnected"];
    }

    // ---------- localisation ----------

    // private Dictionary<string, string> LoadButtonText(string path)
    // {
    //     var result = new Dictionary<string, string>();

    //     if (!File.Exists(path))
    //     {
    //         Console.WriteLine($"Warning: text file not found: {path}");
    //         return result;
    //     }

    //     var lines = File.ReadAllLines(path);
    //     for (int i = 1; i < lines.Length; i++) // skip header row
    //     {
    //         var line = lines[i].Trim();
    //         if (string.IsNullOrEmpty(line)) continue;

    //         var parts = line.Split(new[] { ',' }, 2);
    //         if (parts.Length < 2) continue;

    //         result[parts[0]] = parts[1];
    //     }

    //     return result;
    // }

    // private void ApplyLanguage(bool isEnglish)
    // {
    //     var text = isEnglish ? _engText : _jpnText;
    //     string cssPath = isEnglish ? EngCssPath : JpnCssPath;

    //     foreach (var kvp in _buttonsById)
    //     {
    //         if (text.TryGetValue(kvp.Key, out var buttonText))
    //             kvp.Value.Label = buttonText;
    //     }

    //     if (File.Exists(cssPath))
    //         _languageCssProvider.LoadFromPath(cssPath);
    //     else
    //         Console.WriteLine($"Warning: CSS file not found: {cssPath}");

    //     LanguageLabel.Text = isEnglish ? "あ" : "A";
    // }

    // ---------- scaling ----------

    private void OnWindowSizeAllocated(object o, SizeAllocatedArgs args)
    {
        int width = args.Allocation.Width;
        int height = args.Allocation.Height;

        if (width == _lastAppliedWidth) return;
        _lastAppliedWidth = width;

        double scaleX = (double)width / BaseWidth;
        double scaleY = (double)height / BaseHeight;

        ApplyScale(Math.Min(scaleX, scaleY));
    }

    private void ApplyScale(double scale)
    {
        int fontSize    = Math.Max(10, (int)(20 * scale));
        int buttonPadV  = Math.Max(4,  (int)(12 * scale));
        int buttonPadH  = Math.Max(8,  (int)(24 * scale));
        int switchWidth = Math.Max(30, (int)(40 * scale));
        int switchHeight= Math.Max(18, (int)(24 * scale));

        // NOTE: integers + InvariantCulture. Interpolating a double here emitted
        // "40,5px" under ja_JP / de_DE locales and GTK silently dropped the rule.
        string css = string.Format(CultureInfo.InvariantCulture, @"
            grid, label, button, switch {{
                font-size: {0}px;
            }}
            button {{
                padding: {1}px {2}px;
            }}
            switch {{
                min-width: {3}px;
                min-height: {4}px;
            }}
        ", fontSize, buttonPadV, buttonPadH, switchWidth, switchHeight);

        _scaleCssProvider.LoadFromData(css);
    }

    // ---------- lifecycle ----------

    private void OnDeleteEvent(object sender, DeleteEventArgs a)
    {
         if (_mapWindow != null) _mapWindow.StopSession();
        Application.Quit();
        a.RetVal = true;
        // Shutdown();
        // a.RetVal = true;
    }

    private void OnCloseClicked(object sender, EventArgs e)
    {
        Shutdown();
    }

    private void Shutdown()
    {
        _arduino.Disconnect();
        _arduino.Dispose();
        // _settingsWindow?.Destroy();
        Application.Quit();
    }

    private void OnNormalSessionClicked(object sender, EventArgs e)
    {
        Console.WriteLine("Normal Session selected");
        RootNoteBook.CurrentPage = PageSound;
        _soundWindow.Reset();
    }

    private void OnPositionRecordingClicked(object sender, EventArgs e)
    {
        Console.WriteLine("Position Recording Mode selected");
    }

    private void OnExamModeClicked(object sender, EventArgs e)
    {
        Console.WriteLine("Exam Mode selected");
    }

      private void OnLanguageToggled(object sender, EventArgs e)
    {
        _Language.Apply(LanguageToggle.Active);
    }

    // private void ApplyLanguageEverywhere(bool isEnglish)
    // {
    //     Language.LanguageSwitchValue = isEnglish;
    //     _Language.ApplyLanguage(isEnglish);
    //     _settingsWindow?._Language?.ApplyLanguages(isEnglish);
    // }

    private void OnCaseSelected(object sender, CaseDefinition def)
    {
        // _mapWindow.ShowCase(
        //     string.Join(" : ", def.TreePath),          // no DisplayName on CaseDefinition
        //     SoundWindow.AppFile("body/front.png"),
        //     SoundWindow.AppFile(def.MapFront),
        //     SoundWindow.AppFile(def.SoundPath));

        // RootNoteBook.CurrentPage = PageMap;
        _mapWindow.ShowCase(def, string.Join(" : ", def.TreePath));
        RootNoteBook.CurrentPage = PageMap;
    }

    private void OnMapClosed(object sender, EventArgs e)
    {
        RootNoteBook.CurrentPage = PageSound;   // back to disease list
    }
}