/*
* Copyright © 2025 Alain M. (https://github.com/alainm23/planify)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* Authored by: Alain M. <alainmh23@gmail.com>
*/

public class Widgets.TranslationRow : Adw.PreferencesRow {
    private Gtk.Image _icon;
    private Gtk.Label _title_label;
    private Gtk.Label _subtitle_label;
    private Gtk.LevelBar _level_bar;

    public string? icon_name {
        owned get {
            return _icon.icon_name;
        }

        set {
            _icon.icon_name = value;
        }
    }

    public string title {
        owned get {
            return _title_label.label;
        }

        set {
            _title_label.label = value;
        }
    }

    public string subtitle {
        owned get {
            return _subtitle_label.label;
        }

        set {
            _subtitle_label.label = value;
        }
    }

    public double progress {
        get {
            return _level_bar.value;
        }

        set {
            _level_bar.value = value;
        }
    }
    
    public void hide_level_bar () {
        _level_bar.visible = false;
    }
    
    public void show_level_bar () {
        _level_bar.visible = true;
    }

    private string _language_name = "";
    private double _target_percent = 0.0;
    public bool is_ready_to_animate { get; private set; default = false; }
    private bool _animation_played = false;
    
    public void prepare_animation (string language_name, double target_percent) {
        _language_name = language_name;
        _target_percent = target_percent;
        is_ready_to_animate = true;
    }
    
    public void trigger_animation () {
        if (_animation_played || !is_ready_to_animate) {
            return;
        }
        
        _animation_played = true;
        
        var animation_target = new Adw.CallbackAnimationTarget ((val) => {
            _subtitle_label.label = _("<b>%s</b> is %.0f%% complete. You can help finish it!").printf (_language_name, Math.round (val));
            _level_bar.value = val / 100.0;
        });

        var animation = new Adw.TimedAnimation (
            this, 0, _target_percent, 1000,
            animation_target
        ) {
            easing = Adw.Easing.EASE_IN_OUT_QUAD
        };

        animation.play ();
    }

    public async void load_translation_data () {
        try {
            var languages = Intl.get_language_names ();

            bool is_english = false;

            foreach (string lang in languages) {
                if (lang == "C" || lang == "POSIX") {
                    continue;
                }

                string user_lang = lang.contains ("_") ? lang.split ("_")[0] : lang;

                if (user_lang == "en") {
                    is_english = true;
                    break;
                }
            }

            if (is_english) {
                title = _ ("Translations");
                subtitle = _ ("Help make Planify available worldwide");
                hide_level_bar ();
                return;
            }

            var metric = yield get_current_translation_metric ();

            if (metric != null && metric.translated_percent > 0.0) {
                title = _ ("Help improve translations");
                var language_name = ICU.get_display_language (metric.code, metric.code);
                prepare_animation (language_name, metric.translated_percent);
                return;
            }

            string language_name = ICU.get_display_language (metric.code, "en");

            title = _ ("Start a new translation");
            subtitle = _ ("<b>%s</b> is not available yet. Be the first to translate it!").printf (language_name);

            hide_level_bar ();
        } catch (Error e) {
            title = _ ("Translations");
            subtitle = _ ("Help make Planify available worldwide");
            hide_level_bar ();
        }
    }



    /**
     *   Calculates the translation coverage percentage for the current locale by parsing the MO file.
     *
     *   This function searches for the first available MO file (translation catalog) in the system's locale directory, based on the user's language preferences.
     *   It reads the MO file to count the number of translated strings and calculates the translation coverage percentage.
     *
     *   Returns a `TranslationMetric` object or `null` if no translations are available. Throws an `Error` if the MO file is unreadable, corrupted, or invalid.
     *
     *   MO File Format:
     *
     *   | Field                  | Size (bytes) | Description                                           |
     *   |------------------------|--------------|-------------------------------------------------------|
     *   | Magic Number           | 4            | Identifies endianness (`0x950412de` or `0xde120495`). |
     *   | File Format Revision   | 4            | Version of the MO file format (0 or 1).               |
     *   | Number of Strings      | 4            | Number of translated string entries.                  |
     *   | ...                    | ...          | ...                                                   |
     *
     *   More information: https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
     */
    private async Objects.TranslationMetric? get_current_translation_metric () throws Error {
        const uint32 MAGIC_NUMBER_LE = (uint32) 0x950412de;
        const uint32 MAGIC_NUMBER_BE = (uint32) 0xde120495;

        string[] languages = Intl.get_language_names ();
        string? mo_path = null;
        string? current_lang = null;

        foreach (var lang in languages) {
            var path = Path.build_filename (Build.LOCALEDIR, lang, "LC_MESSAGES", Build.GETTEXT_PACKAGE + ".mo");
            if (FileUtils.test (path, FileTest.EXISTS)) {
                mo_path = path;
                current_lang = lang;
                break;
            }
        }

        if (mo_path == null) {
            info ("Current locale has no translations available");
            return null;
        }

        int translated_strings = 0;
        DataInputStream? data_stream = null;
        try {
            var file = File.new_for_path (mo_path);
            var stream = yield file.read_async ();
            data_stream = new DataInputStream (stream);
            data_stream.set_byte_order (DataStreamByteOrder.LITTLE_ENDIAN);

            uint32 magic = data_stream.read_uint32 ();
            if (magic == MAGIC_NUMBER_BE) {
                data_stream.set_byte_order (DataStreamByteOrder.BIG_ENDIAN);
            } else if (magic != MAGIC_NUMBER_LE) {
                warning ("Unrecognized magic number in MO file: %s", mo_path);
                return null;
            }

            var revision_number = data_stream.read_uint32 ();

            if (revision_number != 0 && revision_number != 1) {
                warning ("MO File has an invalid revision number. Revision Number appeares to be %u", revision_number);
            }

            translated_strings = (int) data_stream.read_uint32 () - 1;
        } catch (Error e) {
            warning ("Error reading .mo file: %s", e.message);
            return null;
        } finally {
            if (data_stream != null) {
                try {
                    yield data_stream.close_async ();
                } catch (Error e) {
                    warning ("Error closing stream: %s", e.message);
                }
            }
        }

        if (translated_strings <= 0) {
            warning ("Invalid number of translated strings in MO file: %s", mo_path);
            return null;
        }

        var metric = new Objects.TranslationMetric ();
        metric.code = current_lang;
        metric.total_strings = Build.TOTAL_STRINGS;
        metric.translated_strings = translated_strings;
        metric.translated_percent = (double) metric.translated_strings / metric.total_strings * 100.0;
        metric.language = GLib.Intl.setlocale (GLib.LocaleCategory.MESSAGES, "");

        return metric;
    }

    construct {
        var main_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12) {
            margin_start = 12,
            margin_end = 12,
            margin_top = 8,
            margin_bottom = 8
        };

        _icon = new Gtk.Image () {
            pixel_size = 16,
            valign = Gtk.Align.CENTER
        };

        var text_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 3) {
            hexpand = true,
            valign = Gtk.Align.CENTER
        };

        _title_label = new Gtk.Label (null) {
            hexpand = true,
            xalign = 0,
            wrap = true
        };

        _subtitle_label = new Gtk.Label (null) {
            xalign = 0,
            hexpand = true,
            use_markup = true,
            wrap = true
        };
        _subtitle_label.add_css_class ("dimmed");
        _subtitle_label.add_css_class ("caption");

        _level_bar = new Gtk.LevelBar () {
            hexpand = true,
            margin_top = 6,
            min_value = 0.0,
            max_value = 1.0
        };

        text_box.append (_title_label);
        text_box.append (_subtitle_label);
        text_box.append (_level_bar);

        main_box.append (_icon);
        main_box.append (text_box);

        child = main_box;
    }
}
