/* 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.
 */

/**
 * Shotwell Pluggable Publishing API
 *
 * The Shotwell Pluggable Publishing API allows you to write plugins that upload
 * photos and videos to web services. The Shotwell distribution includes publishing
 * support for three core services: Flickr, Google Photos, and YouTube.
 * To enable Shotwell to connect to additional services, developers like you write
 * publishing plugins, dynamically-loadable shared objects that are linked into the
 * Shotwell process at runtime. Publishing plugins are just one of several kinds of
 * plugins supported by {@link Spit}, the Shotwell Pluggable Interfaces Technology.
 */
namespace Spit.Publishing {

/**
 * The current version of the Pluggable Publishing API
 */
public const int CURRENT_INTERFACE = 0;

/**
 * Defines different kinds of errors that can occur during publishing.
 */
public errordomain PublishingError {
    /**
     * Indicates that no communications channel could be opened to the remote host.
     *
     * This error occurs, for example, when no network connection is available or
     * when a DNS lookup fails.
     */
    NO_ANSWER,

    /**
     * Indicates that a communications channel to the remote host was previously opened, but
     * the remote host can no longer be reached.
     *
     * This error occurs, for example, when the network is disconnected during a publishing
     * interaction.
     */
    COMMUNICATION_FAILED,

    /**
     * Indicates that a communications channel to the remote host was opened and
     * is active, but that messages sent to or from the remote host can't be understood.
     *
     * This error occurs, for example, when attempting to interact with a RESTful host
     * via XML-RPC.
     */
    PROTOCOL_ERROR,

    /**
     * Indicates that the remote host has received a well-formed message that has caused
     * a server-side error.
     *
     * This error occurs, for example, when the remote host receives a message that should
     * be signed but isn't.
     */
    SERVICE_ERROR,

    /**
     * Indicates that the remote host has sent the local client back a well-formed response,
     * but the response can't be understood.
     *
     * This error occurs, for example, when the remote host sends a response in an XML grammar
     * different from that expected by the local client.
     */
    MALFORMED_RESPONSE,

    /**
     * Indicates that the local client can't access a file or files in local storage.
     *
     * This error occurs, for example, when the local client attempts to read binary data
     * out of a photo or video file that doesn't exist.
     */
    LOCAL_FILE_ERROR,

    /**
     * Indicates that the remote host has rejected the session identifier used by the local
     * client as out-of-date. The local client should acquire a new session identifier.
     */
    EXPIRED_SESSION,

    /**
     * Indicates that a secure connection to the remote host cannot be
     * established. This might have various reasons such as expired
     * certificates, invalid certificates, self-signed certificates...
     */
    SSL_FAILED
}

/** 
 * Represents a connection to a publishing service.
 *
 * Developers of publishing plugins provide a class that implements this interface. At
 * any given time, only one Publisher can be running. When a publisher is running, it is
 * allowed to access the network and has exclusive use of the shared user-interface and
 * configuration services provided by the {@link PluginHost}. Publishers are created in
 * a non-running state and do not begin running until start( ) is invoked. Publishers
 * run until stop( ) is invoked.
 */
public interface Publisher : GLib.Object {
    /**
     * Describes the kinds of media a publishing service supports.
     *
     * Values can be masked together, for example: {{{(MediaType.PHOTO | MediaType.VIDEO)}}}
     * indicates that a publishing service supports the upload of both photos and videos.
     */
    public enum MediaType {
        NONE =          0,
        PHOTO =         1 << 0,
        VIDEO =         1 << 1
    }

    /**
     * Returns a {@link Service} object describing the service to which this connects.
     */
    public abstract Service get_service();

    /**
     * Makes this publisher enter the running state and endows it with exclusive access
     * to the shared services provided by the {@link PluginHost}. Through the host’s interface,
     * this publisher can install user interface panes and query configuration information.
     * Only running services should perform network operations.
     */
    public abstract void start();

    /**
     * Returns true if this publisher is in the running state; false otherwise.
     */
    public abstract bool is_running();
    
    /**
     * Causes this publisher to enter a non-running state. This publisher should stop all
     * network operations and cease use of the shared services provided by the {@link PluginHost}.
     */
    public abstract void stop();
    
    //
    // For future expansion.
    //
    protected virtual void reserved0() {}
    protected virtual void reserved1() {}
    protected virtual void reserved2() {}
    protected virtual void reserved3() {}
    protected virtual void reserved4() {}
    protected virtual void reserved5() {}
    protected virtual void reserved6() {}
    protected virtual void reserved7() {}
}

/**
 * Encapsulates a pane that can be installed in the on-screen publishing dialog box to
 * communicate status to and to get information from the user.
 *
 */
public interface DialogPane : GLib.Object {

    /**
     * Describes how the on-screen publishing dialog box should look and behave when an associated
     * pane is installed in the on-screen publishing dialog box.
     */
    public enum GeometryOptions {
    
        /**
         * When the associated pane is installed, the on-screen publishing dialog box will be
         * sized normally and will not allow the user to change its size.
         */
        NONE =          0,

        /**
         * If this bit is set, when the associated pane is installed, the on-screen publishing
         * dialog box will grow to a larger size.
         */
        EXTENDED_SIZE = 1 << 0,

        /**
         * If this bit is set, when the associated pane is installed, the on-screen publishing
         * dialog box will allow the user to change its size.
         */
        RESIZABLE =     1 << 1,

        /**
         * If this bit is set, when the associated pane is installed, the on-screen publishing
         * dialog box will grow to accommodate a full-width 1024 pixel web page. If both
         * EXTENDED_SIZE and COLOSSAL_SIZE are set, EXTENDED_SIZE takes precedence.
         */
        COLOSSAL_SIZE = 1 << 2;
    }

    /**
     * Returns the Gtk.Widget that is this pane's on-screen representation.
     */
    public abstract Gtk.Widget get_widget();
    
    /**
     * Returns a {@link GeometryOptions} bitfield describing how the on-screen publishing dialog
     * box should look and behave when this pane is installed.
     */
    public abstract GeometryOptions get_preferred_geometry();

    /**
     * Invoked automatically by Shotwell when this pane has been installed into the on-screen
     * publishing dialog box and become visible to the user.
     */
    public abstract void on_pane_installed();

    /**
     * Invoked automatically by Shotwell when this pane has been removed from the on-screen
     * publishing dialog box and is no longer visible to the user.
     */
    public abstract void on_pane_uninstalled();
    
    //
    // For future expansion.
    //
    protected virtual void reserved0() {}
    protected virtual void reserved1() {}
    protected virtual void reserved2() {}
    protected virtual void reserved3() {}
    protected virtual void reserved4() {}
    protected virtual void reserved5() {}
    protected virtual void reserved6() {}
    protected virtual void reserved7() {}
}

/**
 * Enables its caller to report to the user on the progress of a publishing operation.
 *
 * @param file_number the sequence number of media item that the publishing system is currently
 *                    working with, starting at 1. For example, if the user chooses to publish
 *                    4 photos, these photos would have sequence numbers 1, 2, 3, and 4.
 *
 * @param fraction_complete the fraction of the current publishing operation that has been
 *                          completed, from 0.0 to 1.0, inclusive.
 */
public delegate void ProgressCallback(int file_number, double fraction_complete);

/**
 * Called by the publishing system when the user clicks the 'Login' button in a service welcome
 * pane.
 */
public delegate void LoginCallback();

/**
 * Manages and provides services for publishing plugins.
 *
 * Implemented inside Shotwell, the PluginHost provides an interface through which the
 * developers of publishing plugins can query and make changes to the publishing
 * environment. For example, through the PluginHost, plugins can get a list of the photos
 * and videos to be published, install and remove user-interface panes in the publishing
 * dialog box, and request that the items to be uploaded be serialized to a temporary
 * directory on disk. Plugins can use the services of the PluginHost only when their
 * {@link Publisher} is in the running state. This ensures that non-running publishers
 * don’t destructively interfere with the actively running publisher.
 */
public interface PluginHost : GLib.Object, Spit.HostInterface {

    /**
     * Specifies the label text on the push button control that appears in the
     * lower-right-hand corner of the on-screen publishing dialog box.
     */
    public enum ButtonMode {
        CLOSE = 0,
        CANCEL = 1
    }

    public abstract string get_current_profile_id();

    /**
     * Notifies the user that an unrecoverable publishing error has occurred and halts
     * the publishing process.
     *
     * @param err An error object that describes the kind of error that occurred.
     */
    public abstract void post_error(Error err);

    /**
     * Halts the publishing process.
     *
     * Calling this method stops all network activity and hides the on-screen publishing
     * dialog box.
     */
    public abstract void stop_publishing();

    /**
     * Returns a reference to the {@link Publisher} object that this is currently hosting.
     */
    public abstract Publisher get_publisher();

    /**
     * Attempts to install a pane in the on-screen publishing dialog box, making the pane visible
     * and allowing it to interact with the user.
     *
     * If an error has posted, the {@link PluginHost} will not honor this request.
     * 
     * @param pane the pane to install
     *
     * @param mode allows you to set the text displayed on the close/cancel button in the
     * lower-right-hand corner of the on-screen publishing dialog box when pane is installed.
     * If mode is ButtonMode.CLOSE, the button will have the title "Close." If mode is
     * ButtonMode.CANCEL, the button will be titled "Cancel." You should set mode depending on
     * whether a cancellable action is in progress. For example, if your publisher is in the
     * middle of uploading 3 of 8 videos, then mode should be ButtonMode.CANCEL. However, if
     * the publishing operation has completed and the success pane is displayed, then mode
     * should be ButtonMode.CLOSE, because all cancellable publishing actions have already
     * occurred.
     */
    public abstract void install_dialog_pane(Spit.Publishing.DialogPane pane,
        ButtonMode mode = ButtonMode.CANCEL);

    /**
     * Attempts to install a pane in the on-screen publishing dialog box that contains
     * static text.
     *
     * The text appears centered in the publishing dialog box and is drawn in
     * the system font. This is a convenience method only; similar results could be
     * achieved by manually constructing a Gtk.Label widget, wrapping it inside a
     * {@link DialogPane}, and installing it manually with a call to
     * install_dialog_pane( ). To provide visual consistency across publishing services,
     * however, always use this convenience method instead of constructing label panes when
     * you need to display static text to the user.
     *
     * If an error has posted, the {@link PluginHost} will not honor this request.
     * 
     * @param message the text to show in the pane
     *
     * @param mode allows you to set the text displayed on the close/cancel button in the
     * lower-right-hand corner of the on-screen publishing dialog box when pane is installed.
     * If mode is ButtonMode.CLOSE, the button will have the title "Close." If mode is
     * ButtonMode.CANCEL, the button will be titled "Cancel." You should set mode depending on
     * whether a cancellable action is in progress. For example, if your publisher is in the
     * middle of uploading 3 of 8 videos, then mode should be ButtonMode.CANCEL. However, if
     * the publishing operation has completed and the success pane is displayed, then mode
     * should be ButtonMode.CLOSE, because all cancellable publishing actions have already
     * occurred.
     */
    public abstract void install_static_message_pane(string message,
        ButtonMode mode = ButtonMode.CANCEL);

    /**
     * Works just like {@link install_static_message_pane} but allows markup to contain
     * Pango text formatting tags as well as unstyled text.
     *
     * If an error has posted, the {@link PluginHost} will not honor this request.
     * 
     * @param markup the text to show in the pane, marked up with Pango formatting tags.
     *
     * @param mode allows you to set the text displayed on the close/cancel button in the
     * lower-right-hand corner of the on-screen publishing dialog box when pane is installed.
     * If mode is ButtonMode.CLOSE, the button will have the title "Close." If mode is
     * ButtonMode.CANCEL, the button will be titled "Cancel." You should set mode depending on
     * whether a cancellable action is in progress. For example, if your publisher is in the
     * middle of uploading 3 of 8 videos, then mode should be ButtonMode.CANCEL. However, if
     * the publishing operation has completed and the success pane is displayed, then mode
     * should be ButtonMode.CLOSE, because all cancellable publishing actions have already
     * occurred.
     */
    public abstract void install_pango_message_pane(string markup,
        ButtonMode mode = ButtonMode.CANCEL);

    /**
     * Attempts to install a pane in the on-screen publishing dialog box notifying the user
     * that his or her publishing operation completed successfully.
     * 
     * The text displayed depends on the type of media the current publishing service
     * supports. To provide visual consistency across publishing services and to allow
     * Shotwell to handle internationalization, always use this convenience method; don’t
     * construct and install success panes manually.
     *
     * If an error has posted, the {@link PluginHost} will not honor
     * this request.
     */
    public abstract void install_success_pane();

    /**
     * Attempts to install a pane displaying the static text “Fetching account information...”
     * in the on-screen publishing dialog box, making it visible to the user.
     *
     * This is a convenience method only; similar results could be achieved by calling
     * {@link install_static_message_pane} with an appropriate text argument. To provide
     * visual consistency across publishing services and to allow Shotwell to handle
     * internationalization, however, you should always use this convenience method whenever
     * you need to tell the user that you’re querying account information over the network.
     * Queries such as this are almost always performed immediately after the user has logged
     * in to the remote service.
     * 
     * If an error has posted, the {@link PluginHost} will not honor this request.
     */
    public abstract void install_account_fetch_wait_pane();


    /**
     * Works just like {@link install_account_fetch_wait_pane} but displays the static text
     * “Logging in...“
     * 
     * As with {@link install_account_fetch_wait_pane}, this is a convenience method, but
     * you should you use it provide to visual consistency and to let Shotwell handle
     * internationalization. See the description of {@link install_account_fetch_wait_pane}
     * for more information.
     *
     * If an error has posted, the {@link PluginHost} will not honor this request.
     */
    public abstract void install_login_wait_pane();

    /**
     * Attempts to install a pane displaying the text 'welcome_message' above a push
     * button labeled “Login” in the on-screen publishing dialog box, making it visible to the
     * user.
     *
     * When the user clicks the “Login” button, you’ll be notified of the user’s action through
     * the callback 'on_login_clicked'. Every Publisher should provide a welcome pane to
     * introduce the service and explain service-specific features or restrictions. To provide
     * visual consistency across publishing services and to allow Shotwell to handle
     * internationalization, always use this convenience method; don’t construct and install
     * welcome panes manually.
     *
     * If an error has posted, the {@link PluginHost} will not honor this request.
     *
     * @param welcome_message the text to be displayed above a push button labeled “Login”
     * in the on-screen publishing dialog box.
     *
     * @param on_login_clicked specifies the callback that is invoked when the user clicks
     * the “Login” button.
     */
    public abstract void install_welcome_pane(string welcome_message,
        LoginCallback on_login_clicked);

    /**
     * Toggles whether the service selector combo box in the upper-right-hand corner of the
     * on-screen publishing dialog box is sensitive to input.
     *
     * Publishers should make the service selector box insensitive to input when they are performing
     * non-interruptible file or network operations, since switching to another publishing
     * service will halt whatever service is currently running. Under certain circumstances,
     * the {@link PluginHost} may not honor this request.
     *
     * @param is_locked when is_locked is true, the service selector combo box is made insensitive.
     * It appears greyed out and the user is prevented from switching to another publishing service.
     * When is_locked is false, the combo box is sensitive, allowing the user to freely switch
     * from the current service to another service. 
     */
    public abstract void set_service_locked(bool is_locked);

    /**
     * Makes the designated widget the default widget for the publishing dialog.
     *
     * After a call to this method, the designated widget will be activated whenever the user
     * presses the [ENTER] key anywhere in the on-screen publishing dialog box. Under certain
     * circumstances, the {@link PluginHost} may not honor this request.
     *
     * @param widget a reference to the widget to designate as the default widget for the
     *               publishing dialog.
     */
    public abstract void set_dialog_default_widget(Gtk.Widget widget);

    /**
     * Returns an array of the publishable media items that the user has selected for upload to the
     * remote service.
     */
    public abstract Publishable[] get_publishables();

    /**
     * Writes all of the publishable media items that the user has selected for upload to the
     * remote service to a temporary directory on a local disk.
     *
     * You should call this method immediately before sending the publishable media items to the
     * remote service over the network. Because serializing several megabytes of data is a
     * potentially lengthy operation, calling this method installs an activity status pane in
     * the on-screen publishing dialog box. The activity status pane displays a progress bar along
     * with a string of informational text.
     *
     * Because sending items over the network to the remote service is also a potentially lengthy
     * operation, you should leave the activity status pane installed in the on-screen publishing
     * dialog box until this task is finished. Periodically during the sending process, you should
     * report to the user on the progress of his or her upload. You can do this by invoking the
     * returned {@link ProgressCallback} delegate.
     *
     * After calling this method, the activity status pane that this method installs remains
     * displayed in the on-screen publishing dialog box until you install a new pane.
     *
     * @param content_major_axis when serializing publishable media items that are photos,
     *                           ensure that neither the width nor the height of the serialized
     *                           photo is greater than content_major_axis pixels. The value of
     *                           this parameter has no effect on video publishables.
     *
     * @param strip_metadata when serializing publishable media items that are photos, if
     *                       strip_metadata is true, all EXIF, IPTC, and XMP metadata will be
     *                       removed from the serialized file. If strip_metadata is false, all
     *                       metadata will be left intact. The value of this parameter has no
     *                       effect on video publishables.
     */
    public abstract ProgressCallback? serialize_publishables(int content_major_axis,
        bool strip_metadata = false);

    /**
     * Returns a {@link Publisher.MediaType} bitfield describing which kinds of media are present
     * in the set of publishable media items that the user has selected for upload to the remote
     * service.
     */
    public abstract Spit.Publishing.Publisher.MediaType get_publishable_media_type();
    
    //
    // For future expansion.
    //
    protected virtual void reserved0() {}
    protected virtual void reserved1() {}
    protected virtual void reserved2() {}
    protected virtual void reserved3() {}
    protected virtual void reserved4() {}
    protected virtual void reserved5() {}
    protected virtual void reserved6() {}
    protected virtual void reserved7() {}
}

/**
 * Describes an underlying media item (such as a photo or a video) that your plugin
 * uploads to a remote publishing service.
 */
public interface Publishable : GLib.Object {

    public const string PARAM_STRING_BASENAME    = "basename";
    public const string PARAM_STRING_TITLE       = "title";
    public const string PARAM_STRING_COMMENT     = "comment";
    public const string PARAM_STRING_EVENTCOMMENT= "eventcomment";

    /**
     * Returns a handle to the file on disk to which this publishable's data has been
     * serialized.
     *
     * You should use this file handle to read into memory the binary data you will send over
     * the network to the remote publishing service when this publishable is uploaded.
     */
    public abstract GLib.File? get_serialized_file();

    /**
     * Returns a name that can be used to identify this publishable to the remote service.
     * If the publishing host cannot derive a sensible name, this method will
     * return an empty string. Plugins should be able to handle that situation
     * and provide a fallback value. One possible option for a fallback is:
     * get_param_string(Spit.Publishing.Publishable.PARAM_STRING_BASENAME)
     */
    public abstract string get_publishing_name();

    /**
     * Returns a string value from the publishable corresponding with the parameter name 
     * provided, or null if there is no value for this name.
     */
    public abstract string? get_param_string(string name);

    /**
     * Returns an array of strings that should be used to tag or mark this publishable on the
     * remote service, or null if this publishable has no tags or markings.
     */
    public abstract string[] get_publishing_keywords();

    /**
     * Returns the kind of media item this publishable encapsulates.
     */
    public abstract Spit.Publishing.Publisher.MediaType get_media_type();
    
    /**
     * Returns the creation timestamp on the file.
     */
    public abstract GLib.DateTime get_exposure_date_time();
    
    /**
     * Returns the rating on the file.
     */
    public abstract uint get_rating();

    //
    // For future expansion.
    //
    protected virtual void reserved0() {}
    protected virtual void reserved1() {}
    protected virtual void reserved2() {}
    protected virtual void reserved3() {}
    protected virtual void reserved4() {}
    protected virtual void reserved5() {}
    protected virtual void reserved6() {}
    protected virtual void reserved7() {}
}

public interface Account : Object {
    public abstract string display_name();
}

public class DefaultAccount : Spit.Publishing.Account, Object {
    public string display_name() {
        return "";
    }
}


/**
 * Describes the features and capabilities of a remote publishing service.
 *
 * Developers of publishing plugins provide a class that implements this interface.
 */
public interface Service : Object, Spit.Pluggable {
    /**
     * A factory method that instantiates and returns a new {@link Publisher} object that
     * encapsulates a connection to the remote publishing service that this Service describes.
     */
    public abstract Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host);

    public virtual Spit.Publishing.Publisher create_publisher_with_account(Spit.Publishing.PluginHost host,
                                                              Spit.Publishing.Account? account) {
        return this.create_publisher(host);
    }

    /**
     * Returns the kinds of media that this service can work with.
     */
    public abstract Spit.Publishing.Publisher.MediaType get_supported_media();

    /**
     * Returns a list of accounts associated with the service
     * Returns: null if there are no accounts, identifier
     */
    public virtual Gee.List<Account>? get_accounts(string profile_id) {
        var list = new Gee.ArrayList<Account>();
        list.add(new DefaultAccount());

        return list;
    }
    
    //
    // For future expansion.
    //
    protected virtual void reserved0() {}
    protected virtual void reserved1() {}
    protected virtual void reserved2() {}
    protected virtual void reserved3() {}
    protected virtual void reserved4() {}
    protected virtual void reserved5() {}
    protected virtual void reserved6() {}
    protected virtual void reserved7() {}
}

public interface Authenticator : Object {
    public signal void authenticated();
    public signal void authentication_failed();

    public abstract void authenticate();
    public abstract bool can_logout();
    public abstract void logout();
    public abstract void refresh();

    public abstract void set_accountname(string name);

    public abstract GLib.HashTable<string, Variant> get_authentication_parameter();
}

public interface AuthenticatorFactory : Object {
    // By contract, every AuthenticatorFactory implementation needs to have a
    // static get_instance() method. Unfortunately this is not expressable in
    // Vala.

    public abstract Gee.List<string> get_available_authenticators();
    public abstract Authenticator? create(string provider,
            Spit.Publishing.PluginHost host);
}

}