/*
 Copyright (C) 2018 Christian Dywan <christian@twotoats.de>

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 See the file COPYING for the full license text.
*/

namespace Midori {
    [GtkTemplate (ui = "/ui/download-button.ui")]
    public class DownloadButton : Gtk.Button {
        public virtual signal void show_downloads () {
            get_style_context ().remove_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
            popover.show ();
        }

        [GtkChild]
        public Gtk.Popover popover;
        [GtkChild]
        public Gtk.Button clear;
        [GtkChild]
        public Gtk.ListBox listbox;

        string cache = File.new_for_path (Path.build_filename (
            Environment.get_user_cache_dir ())).get_uri ();
        ListStore model = new ListStore (typeof (DownloadItem));

        construct {
            listbox.bind_model (model, create_row);
            popover.relative_to = this;
            clear.clicked.connect (() => {
                hide ();
                model.remove_all ();
            });
        }

        internal WebKit.WebContext web_context { set {
            value.download_started.connect (download_started);
        } }


        void download_started (WebKit.Download download) {
            // Don't show cache files in the UI
            if (download.destination != null && download.destination.has_prefix (cache)) {
                return;
            }
            var item = new DownloadItem.with_download (download);
            item.finished.connect (() => {
                get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
            });
            model.append (item);
        }

        public Gtk.Widget create_row (Object item) {
            visible = true;
            return new DownloadRow ((DownloadItem)item);
        }
    }

    public class DownloadItem : Object {
        public string? mime_type = null;
        public string content_type { owned get {
            string? content_type = ContentType.guess (filename, null, null);
            if (content_type == null) {
                content_type = ContentType.from_mime_type (mime_type);
                if (content_type == null)
                    content_type = ContentType.from_mime_type ("application/octet-stream");
            }
            return content_type;
        } }
        public Icon icon { owned get {
            var icon = GLib.ContentType.get_icon (content_type) as ThemedIcon;
            icon.append_name ("text-html-symbolic");
            return icon;
        } }

        public string? filename { get; protected set; default = null; }
        public string? basename { get; protected set; default = null; }
        public double progress { get; protected set; default = 0.0; }
        public WebKit.Download? download { get; protected set; default = null; }
        public bool loading { get; protected set; default = false; }
        public string? error { get; protected set; default = null; }
        public void cancel () {
            if (download != null) {
                download.cancel ();
                download = null;
                loading = false;
            }
        }
        public signal void finished ();

        construct {
            notify["filename"].connect ((pspec) => {
                if (filename != null) {
                    try {
                        basename = Filename.display_basename (Filename.from_uri (filename));
                    } catch (ConvertError error) {
                        basename = filename;
                        critical ("Failed to convert: %s", error.message);
                    }
                }
            });
        }

        public DownloadItem (string filename) {
            Object (filename: filename);
        }

        public DownloadItem.with_download (WebKit.Download download) {
            Object (download: download, loading: true);
            download.bind_property ("destination", this, "filename", BindingFlags.SYNC_CREATE);
            download.bind_property ("estimated-progress", this, "progress", BindingFlags.SYNC_CREATE);
            download.finished.connect (() => {
                download = null;
                loading = false;
                finished ();
            });
            download.failed.connect ((error) => {
                loading = false;
                this.error = error.message;
            });
        }
    }

    [GtkTemplate (ui = "/ui/download-row.ui")]
    public class DownloadRow : Gtk.ListBoxRow {
        public DownloadItem item { get; protected set; }

        [GtkChild]
        public Gtk.Image icon;
        [GtkChild]
        public Gtk.Label filename;
        [GtkChild]
        public Gtk.ProgressBar progress;
        [GtkChild]
        public Gtk.Button cancel;
        [GtkChild]
        public Gtk.Button open;
        [GtkChild]
        public Gtk.Image error;
        [GtkChild]
        public Gtk.Label status;

        construct {
            cancel.clicked.connect (() => {
                item.cancel ();
            });
            open.clicked.connect (() => {
                try {
                    Gtk.show_uri (get_screen (), item.filename, Gtk.get_current_event_time ());
                } catch (Error error) {
                    status.label = error.message;
                    critical ("Failed to open %s: %s", item.filename, error.message);
                }
            });
        }

        public DownloadRow (DownloadItem item) {
            Object (item: item);
            item.bind_property ("icon", icon, "gicon", BindingFlags.SYNC_CREATE);
            item.bind_property ("basename", filename, "label", BindingFlags.SYNC_CREATE);
            item.bind_property ("basename", filename, "tooltip-text", BindingFlags.SYNC_CREATE);
            item.bind_property ("progress", progress, "fraction", BindingFlags.SYNC_CREATE);
            status.bind_property ("label", status, "tooltip-text", BindingFlags.SYNC_CREATE);
            item.notify["loading"].connect (update_buttons);
            item.notify["error"].connect (update_buttons);
            update_buttons ();

            item.finished.connect (() => {
                var app = (App)Application.get_default ();
                if (item.error == null && app.active_window != null) {
                    var notification = new Notification (_("Transfer completed"));
                    notification.set_icon (item.icon);
                    notification.set_body (item.filename);
                    app.send_notification ("download-finished", notification);
                }
            });
        }

        void update_buttons () {
            progress.visible = item.loading;
            cancel.visible = item.loading;
            open.visible = !item.loading && item.error == null;
            error.visible = item.error != null;
            status.label = item.error ?? "";
            status.visible = item.error != null;
        }
    }
}
