/*
   Copyright (C) 2012 André Stösel <andre@stoesel.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.
*/

using Gtk;
using Katze;
using Midori;

namespace DelayedLoad {
    private class PreferencesDialog : Gtk.Dialog {
        protected Manager dl_manager;
        protected Scale slider;

        public PreferencesDialog (Manager manager) {
            this.dl_manager = manager;

            this.title = _("Preferences for %s").printf ( _("Delayed load"));
            if (this.get_class ().find_property ("has-separator") != null)
                this.set ("has-separator", false);
            this.border_width = 5;
            this.set_modal (true);
            this.set_default_size (350, 100);
            this.create_widgets ();

            this.response.connect (response_cb);
        }

        private void response_cb (Gtk.Dialog source, int response_id) {
            switch (response_id) {
                case ResponseType.APPLY:
                    this.dl_manager.set_integer ("delay", (int) (this.slider.get_value () * 1000));
                    this.dl_manager.preferences_changed ();
                    this.destroy ();
                    break;
                case ResponseType.CANCEL:
                    this.destroy ();
                    break;
            }
        }

        private void create_widgets () {
            Label text = new Label (_("Delay in seconds until loading the page:"));
#if HAVE_GTK3
            this.slider = new Scale.with_range (Orientation.HORIZONTAL, 0, 15, 0.1);
#else
            this.slider = new HScale.with_range (0, 15, 0.1);
#endif

            int delay = this.dl_manager.get_integer ("delay");
            if (delay > 0)
                this.slider.set_value ((float)delay / 1000);

#if HAVE_GTK3
            Gtk.Box vbox = get_content_area () as Gtk.Box;
            vbox.pack_start (text, false, false, 0);
            vbox.pack_start (this.slider, false, true, 0);
#else
            this.vbox.pack_start (text, false, false, 0);
            this.vbox.pack_start (this.slider, false, true, 0);
#endif

            this.add_button (Gtk.STOCK_CANCEL, ResponseType.CANCEL);
            this.add_button (Gtk.STOCK_APPLY, ResponseType.APPLY);

            this.show_all ();
        }
    }

    private class TabShaker : GLib.Object {
        public unowned Midori.Browser browser;
        public GLib.PtrArray tasks;

        public bool reload_tab () {
            if (tasks.len == 1) {
                Midori.View? view = browser.tab as Midori.View;
                Midori.View scheduled_view = tasks.index (0) as Midori.View;
                if (scheduled_view == view) {
                    Katze.Item item = view.get_proxy_item ();
                    item.ref();

                    int64 delay = item.get_meta_integer ("delay");
                    if (delay == Midori.Delay.PENDING_UNDELAY) {
                        view.reload (true);
                    }
                }
            }
            tasks.remove_index (0);
            return false;
        }

        public TabShaker (Midori.Browser browser) {
            this.browser = browser;
        }

        construct {
            this.tasks = new GLib.PtrArray ();
        }
    }

    private class Manager : Midori.Extension {
        private int timeout = 0;
        private HashTable<Midori.Browser, TabShaker> tasks;

        public signal void preferences_changed ();

        private void preferences_changed_cb () {
            this.timeout = get_integer ("delay");
        }

        private void show_preferences () {
            PreferencesDialog dialog = new PreferencesDialog (this);
            dialog.show ();
        }

        private void schedule_reload (Midori.Browser browser, Midori.View view) {
            if (this.timeout == 0)
                view.reload (true);
            else {
                unowned TabShaker shaker = tasks.get (browser);
                if (shaker != null) {
                    shaker.tasks.add (view);
                    Midori.Timeout.add (this.timeout, shaker.reload_tab);
                }
            }
        }

        private void tab_changed (Midori.View? old_view, Midori.View? new_view) {
            if (new_view != null) {
                Midori.App app = get_app ();
                Midori.Browser browser = app.browser;

                Katze.Item item = new_view.get_proxy_item ();
                item.ref();

                int64 delay = item.get_meta_integer ("delay");
                if (delay == Midori.Delay.PENDING_UNDELAY && new_view.progress < 1.0) {
                    this.schedule_reload (browser, new_view);
                }
            }
        }

        private bool reload_first_tab () {
            Midori.App app = get_app ();
            Midori.Browser? browser = app.browser;
            Midori.View? view = browser.tab as Midori.View;

            if (view != null) {
                Katze.Item item = view.get_proxy_item ();
                item.ref();

                int64 delay = item.get_meta_integer ("delay");
                if (delay != Midori.Delay.DELAYED) {
                    WebKit.LoadStatus load_status = view.web_view.load_status;
                    if (load_status == WebKit.LoadStatus.FINISHED) {
                        if (this.timeout != 0)
                            this.tasks.set (browser, new TabShaker (browser));

                        if (view.progress < 1.0)
                            this.schedule_reload (browser, view);

                        return false;
                    }
                }
            }

            return true;
        }

        private void browser_added (Midori.Browser browser) {
            browser.switch_tab.connect_after (this.tab_changed);
        }

        private void browser_removed (Midori.Browser browser) {
            browser.switch_tab.disconnect (this.tab_changed);
        }

        public void activated (Midori.App app) {
            /* FIXME: override behavior without changing the preference */
            app.settings.load_on_startup = MidoriStartup.DELAYED_PAGES;

            this.preferences_changed ();

            Midori.Browser? focused_browser = app.browser;
            if (focused_browser == null)
                Midori.Timeout.add (50, this.reload_first_tab);

            foreach (Midori.Browser browser in app.get_browsers ()) {
                browser_added (browser);
            }
            app.add_browser.connect (browser_added);
        }

        public void deactivated () {
            Midori.App app = get_app ();
            foreach (Midori.Browser browser in app.get_browsers ()) {
                browser_removed (browser);
            }
            app.add_browser.disconnect (browser_added);
        }

        internal Manager () {
            GLib.Object (name: _("Delayed load"),
                         description: _("Delay page load until you actually use the tab."),
                         version: "0.1",
                         authors: "André Stösel <andre@stoesel.de>");

            install_integer ("delay", 0);

            activate.connect (this.activated);
            deactivate.connect (this.deactivated);
            open_preferences.connect (show_preferences);
            preferences_changed.connect (preferences_changed_cb);

            this.tasks = new HashTable<Midori.Browser, TabShaker> (GLib.direct_hash, GLib.direct_equal);
        }
    }
}

public Midori.Extension extension_init () {
    return new DelayedLoad.Manager ();
}

