// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.io;

import static org.openstreetmap.josm.tools.I18n.tr;
import static org.openstreetmap.josm.tools.I18n.trn;

import java.awt.GridBagLayout;
import java.net.Authenticator.RequestorType;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.UserInfo;
import org.openstreetmap.josm.data.preferences.BooleanProperty;
import org.openstreetmap.josm.data.preferences.IntegerProperty;
import org.openstreetmap.josm.gui.JosmUserIdentityManager;
import org.openstreetmap.josm.gui.Notification;
import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.gui.widgets.UrlLabel;
import org.openstreetmap.josm.io.auth.CredentialsAgentException;
import org.openstreetmap.josm.io.auth.CredentialsAgentResponse;
import org.openstreetmap.josm.io.auth.CredentialsManager;
import org.openstreetmap.josm.io.auth.JosmPreferencesCredentialAgent;
import org.openstreetmap.josm.tools.GBC;

/**
 * Notifies user periodically of new received (unread) messages
 * @since 6349
 */
public final class MessageNotifier {

    private MessageNotifier() {
        // Hide default constructor for utils classes
    }

    /** Property defining if this task is enabled or not */
    public static final BooleanProperty PROP_NOTIFIER_ENABLED = new BooleanProperty("message.notifier.enabled", true);
    /** Property defining the update interval in minutes */
    public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("message.notifier.interval", 5);

    private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();

    private static final Runnable WORKER = new Worker();

    private static volatile ScheduledFuture<?> task = null;

    private static class Worker implements Runnable {

        private int lastUnreadCount = 0;

        @Override
        public void run() {
            try {
                final UserInfo userInfo = new OsmServerUserInfoReader().fetchUserInfo(NullProgressMonitor.INSTANCE,
                        tr("get number of unread messages"));
                final int unread = userInfo.getUnreadMessages();
                if (unread > 0 && unread != lastUnreadCount) {
                    GuiHelper.runInEDT(new Runnable() {
                        @Override
                        public void run() {
                            JPanel panel = new JPanel(new GridBagLayout());
                            panel.add(new JLabel(trn("You have {0} unread message.", "You have {0} unread messages.", unread, unread)),
                                    GBC.eol());
                            panel.add(new UrlLabel(Main.getBaseUserUrl() + "/" + userInfo.getDisplayName() + "/inbox",
                                    tr("Click here to see your inbox.")), GBC.eol());
                            panel.setOpaque(false);
                            new Notification().setContent(panel)
                                .setIcon(JOptionPane.INFORMATION_MESSAGE)
                                .setDuration(Notification.TIME_LONG)
                                .show();
                        }
                    });
                    lastUnreadCount = unread;
                }
            } catch (OsmTransferException e) {
                Main.warn(e);
            }
        }
    }

    /**
     * Starts the message notifier task if not already started and if user is fully identified
     */
    public static void start() {
        int interval = PROP_INTERVAL.get();
        if (Main.isOffline(OnlineResource.OSM_API)) {
            Main.info(tr("{0} not available (offline mode)", tr("Message notifier")));
        } else if (!isRunning() && interval > 0 && isUserEnoughIdentified()) {
            task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, interval * 60, TimeUnit.SECONDS);
            Main.info("Message notifier active (checks every "+interval+" minute"+(interval > 1 ? "s" : "")+")");
        }
    }

    /**
     * Stops the message notifier task if started
     */
    public static void stop() {
        if (isRunning()) {
            task.cancel(false);
            Main.info("Message notifier inactive");
            task = null;
        }
    }

    /**
     * Determines if the message notifier is currently running
     * @return {@code true} if the notifier is running, {@code false} otherwise
     */
    public static boolean isRunning() {
        return task != null;
    }

    /**
     * Determines if user set enough information in JOSM preferences to make the request to OSM API without
     * prompting him for a password.
     * @return {@code true} if user chose an OAuth token or supplied both its username and password, {@code false otherwise}
     */
    public static boolean isUserEnoughIdentified() {
        JosmUserIdentityManager identManager = JosmUserIdentityManager.getInstance();
        if (identManager.isFullyIdentified()) {
            return true;
        } else {
            CredentialsManager credManager = CredentialsManager.getInstance();
            try {
                if (JosmPreferencesCredentialAgent.class.equals(credManager.getCredentialsAgentClass())) {
                    if (OsmApi.isUsingOAuth()) {
                        return credManager.lookupOAuthAccessToken() != null;
                    } else {
                        String username = Main.pref.get("osm-server.username", null);
                        String password = Main.pref.get("osm-server.password", null);
                        return username != null && !username.isEmpty() && password != null && !password.isEmpty();
                    }
                } else {
                    CredentialsAgentResponse credentials = credManager.getCredentials(
                            RequestorType.SERVER, OsmApi.getOsmApi().getHost(), false);
                    if (credentials != null) {
                        String username = credentials.getUsername();
                        char[] password = credentials.getPassword();
                        return username != null && !username.isEmpty() && password != null && password.length > 0;
                    }
                }
            } catch (CredentialsAgentException e) {
                Main.warn("Unable to get credentials: "+e.getMessage());
            }
        }
        return false;
    }
}
