/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * 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.
 */
package net.morilib.unix.misc;

import java.util.Arrays;

/**
 * 32ビット文字列をあらわします。
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/03
 */
public final class WideString implements WideCharSequence {

	int[] wchars;

	/**
	 * 32ビット文字列を生成します。
	 * 
	 * @param wchars 文字の配列
	 * @param start  0からの始点
	 * @param count  文字の長さ
	 */
	public WideString(int[] wchars, int start, int count) {
		if(wchars == null) {
			throw new NullPointerException();
		} else if(start < 0 || start >= wchars.length) {
			throw new IndexOutOfBoundsException(start + "");
		} else if(count < 0 || count > wchars.length - start) {
			throw new IndexOutOfBoundsException(count + "");
		}
		this.wchars = new int[count];
		System.arraycopy(wchars, start, this.wchars, 0, count);
	}

	/**
	 * 32ビット文字列を生成します。
	 * 
	 * @param wchars 文字の配列
	 */
	public WideString(int[] wchars) {
		this(wchars, 0, wchars.length);
	}

	private WideString(int[] wchars, boolean dummy) {
		this.wchars = wchars;
	}

	/**
	 * 32ビット文字列を生成します。
	 * 
	 * @param s Java文字列
	 */
	public WideString(String s) {
		int[] wc = new int[s.codePointCount(0, s.length())];
		int c, j = 0, i = 0;

		while(i < s.length()) {
			wc[j++] = (c = s.codePointAt(i));
			i += c > Character.MAX_VALUE ? 2 : 1;
		}
		this.wchars = wc;
	}

	/**
	 * 文字列をコピーします。
	 * 
	 * @param s 32ビット文字列
	 */
	public WideString(WideString s) {
		this(s.wchars, 0, s.length());
	}

	public int charAt(int index) {
		if(index < 0 || index >= wchars.length) {
			throw new StringIndexOutOfBoundsException(index);
		}
		return wchars[index];
	}

	public int length() {
		return wchars.length;
	}

	public WideCharSequence subSequence(int start, int end) {
		return substring(start, end);
	}

	private void chkrange(int start, int end) {
		if(start < 0 || start >= wchars.length) {
			throw new IndexOutOfBoundsException(start + "");
		} else if(end < 0 || end > wchars.length) {
			throw new IndexOutOfBoundsException(end + "");
		} else if(end < start) {
			throw new IndexOutOfBoundsException(end + "");
		}
	}

	/**
	 * 部分文字列を得ます。
	 * 
	 * @param begin  0からの始点
	 * @param end    終点(終点は含みません)
	 * @return 部分文字列
	 */
	public WideString substring(int begin, int end) {
		chkrange(begin, end);
		return new WideString(wchars, begin, end - begin);
	}

	/**
	 * 指定されたところから最後までの部分文字列を得ます。
	 * 
	 * @param begin  0からの始点
	 * @return 部分文字列
	 */
	public WideString substring(int begin) {
		return substring(begin, wchars.length);
	}

	/**
	 * 指定された文字を置き換えます。
	 * 
	 * @param s 置き換える文字列
	 * @param begin  0からの始点
	 * @param end    終点(終点は含みません)
	 * @return 置き換えられた文字列
	 */
	public WideString replace(WideString s, int begin, int end) {
		int[] wt;

		chkrange(begin, end);
		wt = new int[wchars.length - (end - begin) + s.length()];
		System.arraycopy(wchars, 0, wt, 0, begin);
		System.arraycopy(s.wchars, 0, wt, begin, s.length());
		System.arraycopy(wchars, end,
				wt, begin + s.length(), wchars.length - end);
		return new WideString(wt, false);
	}

	/**
	 * 文字列を結合します。
	 * 
	 * @param s 結合する文字列
	 * @return 結合された文字列
	 */
	public WideString concat(WideString s) {
		int[] wt = new int[length() + s.length()];

		System.arraycopy(wchars, 0, wt, 0, length());
		System.arraycopy(s.wchars, 0, wt, length(), s.length());
		return new WideString(wt, false);
	}

	/**
	 * 文字列を結合します。
	 * 
	 * @param strings 結合する文字列
	 * @return 結合された文字列
	 */
	public WideString concat(WideString... strings) {
		WideStringBuilder b = new WideStringBuilder();

		for(WideString s : strings)  b.append(s);
		return b.toWideString();
	}

	@Override
	public int hashCode() {
		return Arrays.hashCode(wchars);
	}

	@Override
	public boolean equals(Object o) {
		if(o instanceof WideString) {
			return Arrays.equals(wchars, ((WideString)o).wchars);
		}
		return false;
	}

	@Override
	public String toString() {
		return new String(wchars, 0, wchars.length);
	}

}
