/*
 * JGrapes Event driven Framework
 * Copyright (C) 2022 Michael N. Lipp
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package org.jgrapes.mail.events;

import jakarta.mail.Flags.Flag;
import jakarta.mail.Folder;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jgrapes.core.Event;
import org.jgrapes.mail.MailChannel;
import org.jgrapes.mail.MailStoreMonitor;

/**
 * Signals the retrieval of mails (update) by a {@link MailStoreMonitor}.
 * Must be fired on a {@link MailChannel}.
 */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class FoldersUpdated extends Event<Void> {

    @SuppressWarnings("PMD.FieldNamingConventions")
    private static final Logger logger
        = Logger.getLogger(FoldersUpdated.class.getName());

    private final List<Folder> folders;
    private final List<Message> newMessages;

    /**
     * Instantiates a new event.
     *
     * @param allMessages the messages
     */
    public FoldersUpdated(List<Folder> folders, List<Message> newMessages) {
        this.folders = folders;
        this.newMessages = newMessages;
    }

    /**
     * Returns the folders.
     *
     * @return the list
     */
    public List<Folder> folders() {
        return folders;
    }

    /**
     * Return the new messages. New messages have not been reported
     * before by an event.
     *
     * @return the list
     */
    public List<Message> newMessages() {
        return newMessages;
    }

    /**
     * Execute the action with the given folder. The method ensures that
     * the folder is open.
     *
     * @param folder the folder
     * @param action the action
     * @throws MessagingException the messaging exception
     */
    @SuppressWarnings("PMD.GuardLogStatement")
    public static <R> R withFolder(Folder folder, Function<Folder, R> action)
            throws MessagingException {
        synchronized (folder) {
            if (!folder.isOpen()) {
                logger.fine("Found folder \"" + folder.getFullName()
                    + "\" to be unexpectedly closed.");
                folder.open(Folder.READ_WRITE);
            }
            return action.apply(folder);
        }
    }

    /**
     * Return all (or max) messages (which are not deleted) from the folder,
     * starting with the newest message.
     *
     * @param folder the folder
     * @param max the limit
     * @return the message[]
     * @throws MessagingException the messaging exception
     */
    @SuppressWarnings("PMD.CognitiveComplexity")
    public static List<Message> messages(Folder folder, int max)
            throws MessagingException {
        MessagingException[] exception = { null };
        @SuppressWarnings({ "PMD.PrematureDeclaration",
            "PMD.GuardLogStatement" })
        var result = withFolder(folder, f -> {
            List<Message> msgs = new LinkedList<>();
            try {
                int available = folder.getMessageCount();
                int retrieve = Math.min(available, max);
                int start = available - retrieve + 1;
                if (start > available) {
                    return msgs;
                }
                for (var msg : f.getMessages(start, available)) {
                    if (msg.getFlags().contains(Flag.DELETED)) {
                        continue;
                    }
                    msgs.add(0, msg);
                }
                while (start > 1 && msgs.size() < max) {
                    Message msg = f.getMessage(--start);
                    if (msg.getFlags().contains(Flag.DELETED)) {
                        continue;
                    }
                    msgs.add(0, msg);
                }
            } catch (MessagingException e) {
                logger.log(Level.FINE, "Problem getting messages: "
                    + e.getMessage(), e);
                exception[0] = e;
            }
            return msgs;
        });
        if (exception[0] != null) {
            throw exception[0];
        }
        return result;
    }

    /**
     * Return all messages (which are not deleted) from the folder.
     *
     * @param folder the folder
     * @return the message[]
     * @throws MessagingException the messaging exception
     */
    public static List<Message> messages(Folder folder)
            throws MessagingException {
        return messages(folder, Integer.MAX_VALUE);
    }
}

@@ -868,16 +868,61 @@
 essage;%0A
+import jakarta.mail.MessageRemovedException;%0A
 import j
@@ -3006,32 +3006,397 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * Return all messages (which are not deleted) from the folder.%0A     *%0A     * @param folder the folder%0A     * @return the message%5B%5D%0A     * @throws MessagingException the messaging exception%0A     */%0A    public static List%3CMessage%3E messages(Folder folder)%0A            throws MessagingException %7B%0A        return messages(folder, Integer.MAX_VALUE);%0A    %7D%0A%0A
     /**%0A     * R
@@ -4436,139 +4436,28 @@
     
-if (msg.getFlags().contains(Flag.DELETED)) %7B%0A                        continue;%0A                    %7D%0A                    msgs.add(0
+maybeAddMessage(msgs
 , ms
@@ -4616,139 +4616,28 @@
     
-if (msg.getFlags().contains(Flag.DELETED)) %7B%0A                        continue;%0A                    %7D%0A                    msgs.add(0
+maybeAddMessage(msgs
 , ms
@@ -5014,156 +5014,89 @@
     
-/**%0A     * Return all messages (which are not deleted) from the folder.%0A     *%0A     * @param folder the folder%0A     * @return the message%5B%5D%0A     * @
+private static void maybeAddMessage(List%3CMessage%3E msgs, Message msg)%0A            
 thro
@@ -5121,124 +5121,150 @@
 ion 
-the messaging exception
+%7B%0A        try %7B
 %0A     
-*/%0A
     
-public static List%3CMessage%3E messages(Folder folder)%0A            throws Messaging
+   if (msg.getFlags().contains(Flag.DELETED)) %7B%0A                return;%0A            %7D%0A        %7D catch (MessageRemoved
 Exce
@@ -5261,34 +5261,41 @@
 emovedException 
-%7B%0A
+e) %7B%0A    
         return m
@@ -5296,43 +5296,43 @@
 turn
- messages(folder, Integer.MAX_VALUE
+;%0A        %7D%0A        msgs.add(0, msg
 );%0A 

Handle exception locally.