/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2011 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://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/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 packager/legal/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.xml.bind.marshaller;

import java.nio.CharBuffer;
import java.nio.charset.CoderResult;


/**
 * Utility class for dealing with surrogates.
 *
 * @author Mark Reinhold
 * @version 1.11, 03/01/23
 */

class Surrogate {

    private Surrogate() { }

    // UTF-16 surrogate-character ranges
    //
    public static final char MIN_HIGH = '\uD800';
    public static final char MAX_HIGH = '\uDBFF';
    public static final char MIN_LOW  = '\uDC00';
    public static final char MAX_LOW  = '\uDFFF';
    public static final char MIN = MIN_HIGH;
    public static final char MAX = MAX_LOW;

    // Range of UCS-4 values that need surrogates in UTF-16
    //
    public static final int UCS4_MIN = 0x10000;
    public static final int UCS4_MAX = (1 << 20) + UCS4_MIN - 1;

    /**
     * Tells whether or not the given UTF-16 value is a high surrogate.
     */
    public static boolean isHigh(int c) {
	return (MIN_HIGH <= c) && (c <= MAX_HIGH);
    }

    /**
     * Tells whether or not the given UTF-16 value is a low surrogate.
     */
    public static boolean isLow(int c) {
	return (MIN_LOW <= c) && (c <= MAX_LOW);
    }

    /**
     * Tells whether or not the given UTF-16 value is a surrogate character,
     */
    public static boolean is(int c) {
	return (MIN <= c) && (c <= MAX);
    }

    /**
     * Tells whether or not the given UCS-4 character must be represented as a
     * surrogate pair in UTF-16.
     */
    public static boolean neededFor(int uc) {
	return (uc >= UCS4_MIN) && (uc <= UCS4_MAX);
    }

    /**
     * Returns the high UTF-16 surrogate for the given UCS-4 character.
     */
    public static char high(int uc) {
	return (char)(0xd800 | (((uc - UCS4_MIN) >> 10) & 0x3ff));
    }

    /**
     * Returns the low UTF-16 surrogate for the given UCS-4 character.
     */
    public static char low(int uc) {
	return (char)(0xdc00 | ((uc - UCS4_MIN) & 0x3ff));
    }

    /**
     * Converts the given surrogate pair into a 32-bit UCS-4 character.
     */
    public static int toUCS4(char c, char d) {
	return (((c & 0x3ff) << 10) | (d & 0x3ff)) + 0x10000;
    }

    /**
     * Surrogate parsing support.  Charset implementations may use instances of
     * this class to handle the details of parsing UTF-16 surrogate pairs.
     */
    public static class Parser {

	public Parser() { }

	private int character;		// UCS-4
	private CoderResult error = CoderResult.UNDERFLOW;
	private boolean isPair;

	/**
	 * Returns the UCS-4 character previously parsed.
	 */
	public int character() {
	    return character;
	}

	/**
	 * Tells whether or not the previously-parsed UCS-4 character was
	 * originally represented by a surrogate pair.
	 */
	public boolean isPair() {
	    return isPair;
	}

	/**
	 * Returns the number of UTF-16 characters consumed by the previous
	 * parse.
	 */
	public int increment() {
	    return isPair ? 2 : 1;
	}

	/**
	 * If the previous parse operation detected an error, return the object
	 * describing that error.
	 */
	public CoderResult error() {
	    return error;
	}

	/**
	 * Returns an unmappable-input result object, with the appropriate
	 * input length, for the previously-parsed character.
	 */
	public CoderResult unmappableResult() {
	    return CoderResult.unmappableForLength(isPair ? 2 : 1);
	}

	/**
	 * Parses a UCS-4 character from the given source buffer, handling
	 * surrogates.
	 *
	 * @param  c    The first character
	 * @param  in   The source buffer, from which one more character
	 *              will be consumed if c is a high surrogate
	 *
	 * @returns  Either a parsed UCS-4 character, in which case the isPair()
	 *           and increment() methods will return meaningful values, or
	 *           -1, in which case error() will return a descriptive result
	 *           object
	 */
	public int parse(char c, CharBuffer in) {
	    if (Surrogate.isHigh(c)) {
		if (!in.hasRemaining()) {
		    error = CoderResult.UNDERFLOW;
		    return -1;
		}
		char d = in.get();
		if (Surrogate.isLow(d)) {
		    character = toUCS4(c, d);
		    isPair = true;
		    error = null;
		    return character;
		}
		error = CoderResult.malformedForLength(1);
		return -1;
	    }
	    if (Surrogate.isLow(c)) {
		error = CoderResult.malformedForLength(1);
		return -1;
	    }
	    character = c;
	    isPair = false;
	    error = null;
	    return character;
	}

	/**
	 * Parses a UCS-4 character from the given source buffer, handling
	 * surrogates.
	 *
	 * @param  c    The first character
	 * @param  ia   The input array, from which one more character
	 *              will be consumed if c is a high surrogate
	 * @param  ip   The input index
	 * @param  il   The input limit
	 *
	 * @returns  Either a parsed UCS-4 character, in which case the isPair()
	 *           and increment() methods will return meaningful values, or
	 *           -1, in which case error() will return a descriptive result
	 *           object
	 */
	public int parse(char c, char[] ia, int ip, int il) {
	    if (Surrogate.isHigh(c)) {
		if (il - ip < 2) {
		    error = CoderResult.UNDERFLOW;
		    return -1;
		}
		char d = ia[ip + 1];
		if (Surrogate.isLow(d)) {
		    character = toUCS4(c, d);
		    isPair = true;
		    error = null;
		    return character;
		}
		error = CoderResult.malformedForLength(1);
		return -1;
	    }
	    if (Surrogate.isLow(c)) {
		error = CoderResult.malformedForLength(1);
		return -1;
	    }
	    character = c;
	    isPair = false;
	    error = null;
	    return character;
	}

    }

    /**
     * Surrogate generation support.  Charset implementations may use instances
     * of this class to handle the details of generating UTF-16 surrogate
     * pairs.
     */
    public static class Generator {

	public Generator() { }

	private CoderResult error = CoderResult.OVERFLOW;

	/**
	 * If the previous generation operation detected an error, return the
	 * object describing that error.
	 */
	public CoderResult error() {
	    return error;
	}

	/**
	 * Generates one or two UTF-16 characters to represent the given UCS-4
	 * character.
	 *
	 * @param  uc   The UCS-4 character
	 * @param  len  The number of input bytes from which the UCS-4 value
	 *              was constructed (used when creating result objects)
	 * @param  dst  The destination buffer, to which one or two UTF-16
	 *              characters will be written
	 *
	 * @returns  Either a positive count of the number of UTF-16 characters
	 *           written to the destination buffer, or -1, in which case
	 *           error() will return a descriptive result object
	 */
	public int generate(int uc, int len, CharBuffer dst) {
	    if (uc <= 0xffff) {
		if (Surrogate.is(uc)) {
		    error = CoderResult.malformedForLength(len);
		    return -1;
		}
		if (dst.remaining() < 1) {
		    error = CoderResult.OVERFLOW;
		    return -1;
		}
		dst.put((char)uc);
		error = null;
		return 1;
	    }
	    if (uc < Surrogate.UCS4_MIN) {
		error = CoderResult.malformedForLength(len);
		return -1;
	    }
	    if (uc <= Surrogate.UCS4_MAX) {
		if (dst.remaining() < 2) {
		    error = CoderResult.OVERFLOW;
		    return -1;
		}
		dst.put(Surrogate.high(uc));
		dst.put(Surrogate.low(uc));
		error = null;
		return 2;
	    }
	    error = CoderResult.unmappableForLength(len);
	    return -1;
	}

	/**
	 * Generates one or two UTF-16 characters to represent the given UCS-4
	 * character.
	 *
	 * @param  uc   The UCS-4 character
	 * @param  len  The number of input bytes from which the UCS-4 value
	 *              was constructed (used when creating result objects)
	 * @param  da   The destination array, to which one or two UTF-16
	 *              characters will be written
	 * @param  dp   The destination position
	 * @param  dl   The destination limit
	 *
	 * @returns  Either a positive count of the number of UTF-16 characters
	 *           written to the destination buffer, or -1, in which case
	 *           error() will return a descriptive result object
	 */
	public int generate(int uc, int len, char[] da, int dp, int dl) {
	    if (uc <= 0xffff) {
		if (Surrogate.is(uc)) {
		    error = CoderResult.malformedForLength(len);
		    return -1;
		}
		if (dl - dp < 1) {
		    error = CoderResult.OVERFLOW;
		    return -1;
		}
		da[dp] = (char)uc;
		error = null;
		return 1;
	    }
	    if (uc < Surrogate.UCS4_MIN) {
		error = CoderResult.malformedForLength(len);
		return -1;
	    }
	    if (uc <= Surrogate.UCS4_MAX) {
		if (dl - dp < 2) {
		    error = CoderResult.OVERFLOW;
		    return -1;
		}
		da[dp] = Surrogate.high(uc);
		da[dp + 1] = Surrogate.low(uc);
		error = null;
		return 2;
	    }
	    error = CoderResult.unmappableForLength(len);
	    return -1;
	}

    }

}
