/*******************************************************************************
 * Copyright 2015 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

/*******************************************************************************
 * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 *  IN THE SOFTWARE.
 ******************************************************************************/

package com.badlogic.gdx.utils;

/** Utf8Decoder converts UTF-8 encoded bytes into characters properly handling buffer boundaries.
 *
 * This class is stateful and up to 4 calls to {@link #decode(byte)} may be needed before a character is appended to the char
 * buffer.
 *
 * The UTF-8 decoding is done by this class and no additional buffers are created. The UTF-8 code was inspired by
 * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
 * 
 * @author davebaol */
public class Utf8Decoder {

	private static final char REPLACEMENT = '\ufffd';
	private static final int UTF8_ACCEPT = 0;
	private static final int UTF8_REJECT = 12;

	// This table maps bytes to character classes to reduce
	// the size of the transition table and create bitmasks.
	private static final byte[] BYTE_TABLE = {
		// @off - disable libGDX formatter
		 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
		 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
		 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
		10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8
		// @on - enable libGDX formatter
	};

	// This is a transition table that maps a combination of a
	// state of the automaton and a character class to a state.
	private static final byte[] TRANSITION_TABLE = {
		// @off - disable libGDX formatter
		 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
		12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
		12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
		12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
		12,36,12,12,12,12,12,12,12,12,12,12
		// @on - enable libGDX formatter
	};

	private int codePoint;
	private int state;
	private final char[] utf16Char = new char[2];
	private char[] charBuffer;
	private int charOffset;

	public Utf8Decoder () {
		this.state = UTF8_ACCEPT;
	}

	protected void reset () {
		state = UTF8_ACCEPT;
	}

	public int decode (byte[] b, int offset, int length, char[] charBuffer, int charOffset) {
		this.charBuffer = charBuffer;
		this.charOffset = charOffset;
		int end = offset + length;
		for (int i = offset; i < end; i++)
			decode(b[i]);
		return this.charOffset - charOffset;
	}

	private void decode (byte b) {

		if (b > 0 && state == UTF8_ACCEPT) {
			charBuffer[charOffset++] = (char)(b & 0xFF);
		} else {
			int i = b & 0xFF;
			int type = BYTE_TABLE[i];
			codePoint = state == UTF8_ACCEPT ? (0xFF >> type) & i : (i & 0x3F) | (codePoint << 6);
			int next = TRANSITION_TABLE[state + type];

			switch (next) {
			case UTF8_ACCEPT:
				state = next;
				if (codePoint < Character.MIN_HIGH_SURROGATE) {
					charBuffer[charOffset++] = (char)codePoint;
				} else {
					// The code below is equivalent to
					// for (char c : Character.toChars(codePoint)) charBuffer[charOffset++] = c;
					// but does not allocate a char array.
					int codePointLength = Character.toChars(codePoint, utf16Char, 0);
					charBuffer[charOffset++] = utf16Char[0];
					if (codePointLength == 2) charBuffer[charOffset++] = utf16Char[1];
				}
				break;

			case UTF8_REJECT:
				codePoint = 0;
				state = UTF8_ACCEPT;
				charBuffer[charOffset++] = REPLACEMENT;
				break;

			default:
				state = next;
				break;
			}
		}
	}
}
