/*
 * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.xbib.net.mail.imap.protocol;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * This class holds the 'start' and 'end' for a range of UIDs.
 * Just like MessageSet except using long instead of int.
 */
public class UIDSet {

    public long start;
    public long end;

    public UIDSet() {
    }

    public UIDSet(long start, long end) {
        this.start = start;
        this.end = end;
    }

    /**
     * Count the total number of elements in a UIDSet
     *
     * @return the number of elements
     */
    public long size() {
        return end - start + 1;
    }

    /**
     * Convert an array of longs into an array of UIDSets
     *
     * @param    uids    the UIDs
     * @return array of UIDSet objects
     */
    public static UIDSet[] createUIDSets(long[] uids) {
        if (uids == null)
            return null;
        List<UIDSet> v = new ArrayList<>();
        int i, j;

        for (i = 0; i < uids.length; i++) {
            UIDSet ms = new UIDSet();
            ms.start = uids[i];

            // Look for contiguous elements
            for (j = i + 1; j < uids.length; j++) {
                if (uids[j] != uids[j - 1] + 1)
                    break;
            }
            ms.end = uids[j - 1];
            v.add(ms);
            i = j - 1; // i gets incremented @ top of the loop
        }
        UIDSet[] uidset = new UIDSet[v.size()];
        return v.toArray(uidset);
    }

    /**
     * Parse a string in IMAP UID range format.
     *
     * @param    uids    UID string
     * @return array of UIDSet objects
     * @since JavaMail 1.5.1
     */
    public static UIDSet[] parseUIDSets(String uids) {
        if (uids == null)
            return null;
        List<UIDSet> v = new ArrayList<>();
        StringTokenizer st = new StringTokenizer(uids, ",:", true);
        UIDSet cur = null;
        try {
            while (st.hasMoreTokens()) {
                String s = st.nextToken();
                if (s.equals(",")) {
                    if (cur != null)
                        v.add(cur);
                    cur = null;
                } else if (s.equals(":")) {
                    // nothing to do, wait for next number
                } else {    // better be a number
                    long n = Long.parseLong(s);
                    if (cur != null)
                        cur.end = n;
                    else
                        cur = new UIDSet(n, n);
                }
            }
        } catch (NumberFormatException nex) {
            // give up and return what we have so far
        }
        if (cur != null)
            v.add(cur);
        UIDSet[] uidset = new UIDSet[v.size()];
        return v.toArray(uidset);
    }

    /**
     * Convert an array of UIDSets into an IMAP sequence range.
     *
     * @param    uidset    the UIDSets
     * @return the IMAP sequence string
     */
    public static String toString(UIDSet[] uidset) {
        if (uidset == null)
            return null;
        if (uidset.length == 0) // Empty uidset
            return "";

        int i = 0;  // uidset index
        StringBuilder s = new StringBuilder();
        int size = uidset.length;
        long start, end;

        for (; ; ) {
            start = uidset[i].start;
            end = uidset[i].end;

            if (end > start)
                s.append(start).append(':').append(end);
            else // end == start means only one element
                s.append(start);

            i++; // Next UIDSet
            if (i >= size) // No more UIDSets
                break;
            else
                s.append(',');
        }
        return s.toString();
    }

    /**
     * Convert an array of UIDSets into a array of long UIDs.
     *
     * @param    uidset    the UIDSets
     * @return arrray of UIDs
     * @since JavaMail 1.5.1
     */
    public static long[] toArray(UIDSet[] uidset) {
        //return toArray(uidset, -1);
        if (uidset == null)
            return null;
        long[] uids = new long[(int) UIDSet.size(uidset)];
        int i = 0;
        for (UIDSet u : uidset) {
            for (long n = u.start; n <= u.end; n++)
                uids[i++] = n;
        }
        return uids;
    }

    /**
     * Convert an array of UIDSets into a array of long UIDs.
     * Don't include any UIDs larger than uidmax.
     *
     * @param    uidset    the UIDSets
     * @param    uidmax    maximum UID
     * @return arrray of UIDs
     * @since JavaMail 1.5.1
     */
    public static long[] toArray(UIDSet[] uidset, long uidmax) {
        if (uidset == null)
            return null;
        long[] uids = new long[(int) UIDSet.size(uidset, uidmax)];
        int i = 0;
        for (UIDSet u : uidset) {
            for (long n = u.start; n <= u.end; n++) {
                if (uidmax >= 0 && n > uidmax)
                    break;
                uids[i++] = n;
            }
        }
        return uids;
    }

    /**
     * Count the total number of elements in an array of UIDSets.
     *
     * @param    uidset    the UIDSets
     * @return the number of elements
     */
    public static long size(UIDSet[] uidset) {
        long count = 0;

        if (uidset != null)
            for (UIDSet u : uidset)
                count += u.size();

        return count;
    }

    /**
     * Count the total number of elements in an array of UIDSets.
     * Don't count UIDs greater then uidmax.
     *
     * @since JavaMail 1.5.1
     */
    private static long size(UIDSet[] uidset, long uidmax) {
        long count = 0;

        if (uidset != null)
            for (UIDSet u : uidset) {
                if (uidmax < 0)
                    count += u.size();
                else if (u.start <= uidmax) {
                    if (u.end < uidmax)
                        count += u.end - u.start + 1;
                    else
                        count += uidmax - u.start + 1;
                }
            }

        return count;
    }
}
