/* Copyright 2016 Software Freedom Conservancy Inc.
 *
 * This software is licensed under the GNU Lesser General Public License
 * (version 2.1 or later).  See the COPYING file in this distribution.
 */

namespace Spit.Publishing {

public class ConcretePublishingHost : Plugins.StandardHostInterface,
    Spit.Publishing.PluginHost {
    private const string PREPARE_STATUS_DESCRIPTION = _("Preparing for upload");
    private const string UPLOAD_STATUS_DESCRIPTION = _("Uploading %d of %d");
    private const double STATUS_PREPARATION_FRACTION = 0.3;
    private const double STATUS_UPLOAD_FRACTION = 0.7;
    
    private weak PublishingUI.PublishingDialog dialog = null;
    private Spit.Publishing.Publisher active_publisher = null;
    private Publishable[] publishables = null;
    private unowned LoginCallback current_login_callback = null;
    private bool publishing_halted = false;
    private Spit.Publishing.Publisher.MediaType media_type =
        Spit.Publishing.Publisher.MediaType.NONE;
    
    public ConcretePublishingHost(Service service, PublishingUI.PublishingDialog dialog,
        Publishable[] publishables, Account account) {
        base(service, "sharing");
        this.dialog = dialog;
        this.publishables = publishables;
        
        foreach (Publishable curr_publishable in publishables)
            this.media_type |= curr_publishable.get_media_type();
        
        this.active_publisher = service.create_publisher_with_account(this, account);
    }

    public void register_auth_callback(string cookie, AuthenticatedCallback callback) {
        Application.register_auth_callback(cookie, callback);
    }

    public void unregister_auth_callback(string cookie) {
        Application.unregister_auth_callback(cookie);
    }

    public string get_current_profile_id() {
        return Shotwell.ProfileManager.get_instance().id();
    }
    
    private void on_login_clicked() {
        if (current_login_callback != null)
            current_login_callback();
    }
    
    private void clean_up() {
        foreach (Publishable publishable in publishables)
            ((global::Publishing.Glue.MediaSourcePublishableWrapper) publishable).clean_up();
    }
    
    private void report_plugin_upload_progress(int file_number, double fraction_complete) {
        // if the currently installed pane isn't the progress pane, do nothing
        if (!(dialog.get_active_pane() is PublishingUI.ProgressPane))
            return;

        PublishingUI.ProgressPane pane = (PublishingUI.ProgressPane) dialog.get_active_pane();
        
        string status_string = UPLOAD_STATUS_DESCRIPTION.printf(file_number,
            publishables.length);
        double status_fraction = STATUS_PREPARATION_FRACTION + (STATUS_UPLOAD_FRACTION *
            fraction_complete);
        
        pane.set_status(status_string, status_fraction);
    }

    private void install_progress_pane() {
        PublishingUI.ProgressPane progress_pane = new PublishingUI.ProgressPane();
        
        dialog.install_pane(progress_pane);
        set_button_mode(Spit.Publishing.PluginHost.ButtonMode.CANCEL);
    }

    public void install_dialog_pane(Spit.Publishing.DialogPane pane,
        Spit.Publishing.PluginHost.ButtonMode button_mode) {
        debug("Publishing.PluginHost: install_dialog_pane( ): invoked.");

        if (active_publisher == null || (!active_publisher.is_running()))
            return;

        dialog.install_pane(pane);
        
        set_button_mode(button_mode);
    }
    
    public void post_error(Error err) {
        string msg = _("Publishing to %s can’t continue because an error occurred:").printf(
            active_publisher.get_service().get_pluggable_name());
        msg += GLib.Markup.printf_escaped("\n\n<i>%s</i>\n\n", err.message);
        msg += _("To try publishing to another service, select one from the above menu.");
        
        dialog.install_pane(new PublishingUI.StaticMessagePane(msg, true));
        dialog.set_close_button_mode();
        dialog.unlock_service();

        active_publisher.stop();
        
        // post_error( ) tells the active_publisher to stop publishing and displays a
        // non-removable error pane that effectively ends the publishing interaction,
        // so no problem calling clean_up( ) here.
        clean_up();
    }

    public void stop_publishing() {
        debug("ConcretePublishingHost.stop_publishing( ): invoked.");
        
        if (active_publisher.is_running())
            active_publisher.stop();

        clean_up();

        publishing_halted = true;
    }
    
    public void start_publishing() {
        if (active_publisher.is_running())
            return;

        debug("ConcretePublishingHost.start_publishing( ): invoked.");
        
        active_publisher.start();
    }

    public Publisher get_publisher() {
        return active_publisher;
    }

    public void install_static_message_pane(string message,
        Spit.Publishing.PluginHost.ButtonMode button_mode) {

        set_button_mode(button_mode);

        dialog.install_pane(new PublishingUI.StaticMessagePane(message));
    }
    
    public void install_pango_message_pane(string markup,
        Spit.Publishing.PluginHost.ButtonMode button_mode) {
        set_button_mode(button_mode);

        dialog.install_pane(new PublishingUI.StaticMessagePane(markup, true));
    }
    
    public void install_success_pane() {
        dialog.install_pane(new PublishingUI.SuccessPane(get_publishable_media_type(),
            publishables.length));
        dialog.set_close_button_mode();

        // the success pane is a terminal pane; once it's installed, the publishing
        // interaction is considered over, so clean up
        clean_up();
    }
    
    public void install_account_fetch_wait_pane() {
        dialog.install_pane(new PublishingUI.AccountFetchWaitPane());
        set_button_mode(Spit.Publishing.PluginHost.ButtonMode.CANCEL);
    }
    
    public void install_login_wait_pane() {
        dialog.install_pane(new PublishingUI.LoginWaitPane());
    }
    
    public void install_welcome_pane(string welcome_message, LoginCallback login_clicked_callback) {
        PublishingUI.LoginWelcomePane login_pane =
            new PublishingUI.LoginWelcomePane(welcome_message);
        current_login_callback = login_clicked_callback;
        login_pane.login_requested.connect(on_login_clicked);

        set_button_mode(Spit.Publishing.PluginHost.ButtonMode.CLOSE);

        dialog.install_pane(login_pane);
    }
    
    public void set_service_locked(bool locked) {
        if (locked)
            dialog.lock_service();
        else
            dialog.unlock_service();
    }
    
    public void set_button_mode(Spit.Publishing.PluginHost.ButtonMode mode) {
        if (mode == Spit.Publishing.PluginHost.ButtonMode.CLOSE)
            dialog.set_close_button_mode();
        else if (mode == Spit.Publishing.PluginHost.ButtonMode.CANCEL)
            dialog.set_cancel_button_mode();
        else
            error("unrecognized button mode enumeration value");
    }

    public void set_dialog_default_widget(Gtk.Widget widget) {
        widget.can_default = true;
        dialog.set_default(widget);
    }
    
    public Spit.Publishing.Publisher.MediaType get_publishable_media_type() {
        return media_type;
    }

    public Publishable[] get_publishables() {
        return publishables;
    }
    
    public Spit.Publishing.ProgressCallback? serialize_publishables(int content_major_axis,
        bool strip_metadata = false) {
        install_progress_pane();
        PublishingUI.ProgressPane progress_pane =
            (PublishingUI.ProgressPane) dialog.get_active_pane();

        // spin the event loop right after installing the progress_pane so that the progress_pane
        // will appear and let the user know that something is going on while file serialization
        // takes place
        spin_event_loop();

        int i = 0;
        foreach (Spit.Publishing.Publishable publishable in publishables) {
            if (publishing_halted || !active_publisher.is_running())
                return null;

            try {
                global::Publishing.Glue.MediaSourcePublishableWrapper wrapper =
                    (global::Publishing.Glue.MediaSourcePublishableWrapper) publishable;
                wrapper.serialize_for_publishing(content_major_axis, strip_metadata);
            } catch (Spit.Publishing.PublishingError err) {
                post_error(err);
                return null;
            }

            double phase_fraction_complete = ((double) (i + 1)) / ((double) publishables.length);
            double fraction_complete = phase_fraction_complete * STATUS_PREPARATION_FRACTION;
            
            debug("serialize_publishables( ): fraction_complete = %f.", fraction_complete);
            
            progress_pane.set_status(PREPARE_STATUS_DESCRIPTION, fraction_complete);
            
            spin_event_loop();

            i++;
        }
        
        return report_plugin_upload_progress;
    }
}

}