// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *******************************************************************************
 *   Copyright (C) 2002-2010, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
*/

package com.ibm.icu.impl;
/**
 * @version     1.1
 * @author     Markus W. Scherer
 * Ram: Add documentation, remove unwanted methods, improve coverage.
 */

/**
 * Simple class for handling serialized USet/UnicodeSet structures
 * without object creation. See ICU4C icu/source/common/uset.c.
 *
 * @internal
 */
public final class USerializedSet {
    /**
     * Fill in the given serialized set object.
     * @param src pointer to start of array
     * @param srcStart pointer to start of serialized data (length value)
     * @return true if the given array is valid, otherwise false
     */
    public final boolean getSet(char src[], int srcStart) {
        // leave most argument checking up to Java exceptions
        array=null;
        arrayOffset=bmpLength=length=0;

        length=src[srcStart++];
        
        if ((length&0x8000) != 0) {
            /* there are supplementary values */
            length&=0x7fff;
            if(src.length<(srcStart+1+length)) {
                length=0;
                throw new IndexOutOfBoundsException();
            }
            bmpLength=src[srcStart++];
        } else {
            /* only BMP values */
            if(src.length<(srcStart+length)) {
                length=0;
                throw new IndexOutOfBoundsException();
            }
            bmpLength=length;
        }
        array = new char[length];
        System.arraycopy(src,srcStart,array,0,length);
        //arrayOffset=srcStart;
        return true;
    }
    
    /**
     * Set the USerializedSet to contain the given character (and nothing
     * else).
     */
    public final void setToOne(int c) {
        if( 0x10ffff<c) {
            return;
        }

        if(c<0xffff) {
            bmpLength=length=2;
            array[0]=(char)c;
            array[1]=(char)(c+1);
        } else if(c==0xffff) {
            bmpLength=1;
            length=3;
            array[0]=0xffff;
            array[1]=1;
            array[2]=0;
        } else if(c<0x10ffff) {
            bmpLength=0;
            length=4;
            array[0]=(char)(c>>16);
            array[1]=(char)c;
            ++c;
            array[2]=(char)(c>>16);
            array[3]=(char)c;
        } else /* c==0x10ffff */ {
            bmpLength=0;
            length=2;
            array[0]=0x10;
            array[1]=0xffff;
        }
    }
        
    /**
     * Returns a range of characters contained in the given serialized
     * set.
     * @param rangeIndex a non-negative integer in the range <code>0..
     * getSerializedRangeCount()-1</code>
     * @param range variable to receive the data in the range
     * @return true if rangeIndex is valid, otherwise false
     */
    public final boolean getRange(int rangeIndex, int[] range) {
        if( rangeIndex<0) {
            return false;
        }
        if(array==null){
            array = new char[8];
        }
        if(range==null || range.length <2){
            throw new IllegalArgumentException();
        }
        rangeIndex*=2; /* address start/limit pairs */
        if(rangeIndex<bmpLength) {
            range[0]=array[rangeIndex++];
            if(rangeIndex<bmpLength) {
                range[1]=array[rangeIndex]-1;
            } else if(rangeIndex<length) {
                range[1]=((((int)array[rangeIndex])<<16)|array[rangeIndex+1])-1;
            } else {
                range[1]=0x10ffff;
            }
            return true;
        } else {
            rangeIndex-=bmpLength;
            rangeIndex*=2; /* address pairs of pairs of units */
            int suppLength=length-bmpLength;
            if(rangeIndex<suppLength) {
                int offset=arrayOffset+bmpLength;
                range[0]=(((int)array[offset+rangeIndex])<<16)|array[offset+rangeIndex+1];
                rangeIndex+=2;
                if(rangeIndex<suppLength) {
                    range[1]=((((int)array[offset+rangeIndex])<<16)|array[offset+rangeIndex+1])-1;
                } else {
                    range[1]=0x10ffff;
                }
                return true;
            } else {
                return false;
            }
        }
    }
    
    /**
     * Returns true if the given USerializedSet contains the given
     * character.
     * @param c the character to test for
     * @return true if set contains c
     */
    public final boolean contains(int c) {
        
        if(c>0x10ffff) {
            return false;
        }
            
        if(c<=0xffff) {
            int i;
            /* find c in the BMP part */
            for(i=0; i<bmpLength && (char)c>=array[i]; ++i) {}
            return ((i&1) != 0);
        } else {
            int i;
            /* find c in the supplementary part */
            char high=(char)(c>>16), low=(char)c;
            for(i=bmpLength;
                i<length && (high>array[i] || (high==array[i] && low>=array[i+1]));
                i+=2) {}
        
            /* count pairs of 16-bit units even per BMP and check if the number of pairs is odd */
            return (((i+bmpLength)&2)!=0);
        }
    }

    /**
     * Returns the number of disjoint ranges of characters contained in
     * the given serialized set.  Ignores any strings contained in the
     * set.
     * @return a non-negative integer counting the character ranges
     * contained in set
     */
    public final int countRanges() {
        return (bmpLength+(length-bmpLength)/2+1)/2;
    }
    
    private char array[] = new char[8];
    private int arrayOffset, bmpLength, length;
}
