// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
/*-
 * Copyright (c) 2013-2015 Pantheon Developers (https://launchpad.net/switchboard-plug-onlineaccounts)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authored by: Corentin Noël <corentin@elementary.io>
 */

public class E.GSignonSessionPassword : E.SourceCredentialsProviderImpl {
    Ag.Manager manager;

    construct {
        manager = new Ag.Manager ();
    }

    public override bool can_process (E.Source source) {
        return false;
        var credentials_source = ref_credentials_source (source);
        if (credentials_source == null) {
            return false;
        }

        E.SourceUoa extension;
        if (credentials_source.has_extension (E.SOURCE_EXTENSION_UOA)) {
            var account_service = new_account_service (source);
            if (account_service != null) {
                var auth_data = account_service.get_auth_data ();
                if (auth_data.get_method () == "password" && auth_data.get_mechanism () == "password") {
                    return true;
                }
            }
        }

        return false;
    }

    public override bool can_prompt () {
        return false;
    }

    public override bool can_store () {
        return false;
    }

    public override bool lookup_sync (E.Source source, GLib.Cancellable? cancellable, out E.NamedParameters out_credentials) throws GLib.Error {
        var main_loop = new GLib.MainLoop ();
        Error error = null;
        string password = null;
        get_password.begin (source, (obj, res) => {
            try {
                password = get_password.end (res);
            } catch (Error e) {
                error = e;
            }

            main_loop.quit ();
        });

        main_loop.run ();
        if (error != null) {
            debug ("Complete (ERROR - %s)", error.message);
        } else {
            debug ("Complete (SUCCESS)");
            out_credentials = new E.NamedParameters ();
            out_credentials.set (E.SOURCE_CREDENTIAL_PASSWORD, password);
        }

        return error != null;
    }

    private E.Source ref_credentials_source (E.Source source) {
        var current_source = source;
        while (current_source != null && !current_source.has_extension (E.SOURCE_EXTENSION_UOA)) {
            var parent_id = current_source.get_parent ();
            if (parent_id == null) {
                break;
            }

            current_source = get_provider ().ref_source (parent_id);
        }

        if (current_source != null && current_source.has_extension (E.SOURCE_EXTENSION_UOA)) {
            return current_source;
        }

        if (current_source == null) {
            current_source = get_provider ().ref_credentials_source (source);
        }

        return current_source;
    }

    private async string? get_password (E.Source source) throws GLib.Error {
        try {
            var account_service = new_account_service (source);
            var auth_data = account_service.get_auth_data ();
            var identity = new Signon.Identity.from_db (auth_data.get_credentials_id ());
            var signon_auth_session = identity.create_session ("password");

            /* This returns a floating reference. */
            var login_parameters = auth_data.get_login_parameters (null);

            var session_data = yield signon_auth_session.process_async (login_parameters, "password", null);
            var secret = session_data.lookup_value (Signon.SESSION_DATA_SECRET, GLib.VariantType.STRING);
            if (secret == null) {
                throw new Signon.Error.MISSING_DATA ("Signon service did not return a secret");
            }

            return secret.dup_string ();
        } catch (Error e) {
            throw e;
        }
    }

    private Ag.AccountService? new_account_service (E.Source source) throws GLib.Error {
        E.SourceUoa extension = null;
        var credentials_source = ref_credentials_source (source);
        if (credentials_source != null && credentials_source.has_extension (E.SOURCE_EXTENSION_UOA)) {
            extension = (E.SourceUoa) credentials_source.get_extension (E.SOURCE_EXTENSION_UOA);
        }

        if (extension == null) {
            return null;
        }

        var account_id = extension.get_account_id ();
        Ag.Account ag_account;
        try {
            ag_account = manager.load_account (account_id);
        } catch (Error e) {
            throw e;
        }

        var services = ag_account.list_services ();
        if (services == null) {
            return null;
        }

        return new Ag.AccountService (ag_account, services.data);
    }
}

[ModuleInit]
public static Type e_fake_module_load (GLib.TypeModule type_module) {
    return typeof(E.GSignonSessionPassword);
}
