/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.mail.imap.protocol;

import java.util.List;
import java.util.ArrayList;
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);
	long start = -1;
	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;
    }
}
