/*
 * 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.awk.misc;

/**
 * 文字列を置換するユーティリティクラスです。<br />
 * UNIXのtr(1)コマンド互換の動作をします。
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/02
 */
public class Tr {

	//
	static int indexOf(String s, int c) {
		int x, y;

		for(int i = 0, j = 0; i < s.length(); j++) {
			x  = s.codePointAt(i);
			i += x > 0xffff ? 2 : 1;
			if(c == x) {
				return j;
			} else if(i < s.length() && s.codePointAt(i) == '-') {
				if(c <= (y = s.codePointAt(++i)) && c > x) {
					return j + c - x;
				}
				i += y > 0xffff ? 2 : 1;
				j += y - x;
			}
		}
		return -1;
	}

	//
	static int codePointAt(String s, int index) {
		int x, y;

		for(int i = 0, j = 0; i < s.length(); j++) {
			x  = s.codePointAt(i);
			i += x > 0xffff ? 2 : 1;
			if(index == j) {
				return x;
			} else if(i < s.length() && s.codePointAt(i) == '-') {
				if(index - j <= (y = s.codePointAt(++i)) - x) {
					return x + index - j;
				}
				i += y > 0xffff ? 2 : 1;
				j += y - x;
			}
		}
		return -1;
	}

	//
	static int length(String s) {
		int x, y, j = 0;

		for(int i = 0; i < s.length(); j++) {
			x  = s.codePointAt(i);
			i += x > 0xffff ? 2 : 1;
			if(i < s.length() && s.codePointAt(i) == '-') {
				y = s.codePointAt(++i);
				i += y > 0xffff ? 2 : 1;
				j += y - x;
			}
		}
		return j;
	}

	/**
	 * 文字を入れ替えます。
	 * 
	 * @param source 入替前の文字パターン
	 * @param dest   入替後の文字パターン
	 * @param c      対象の文字
	 * @return       入れ替えた結果の文字
	 */
	public static int tr(String source, String dest, int c) {
		int x, d;

		if(source.length() == 0) {
			return c;
		} else if(dest.length() == 0) {
			return c;
		} else if((x = indexOf(source, c)) < 0) {
			return c;
		} else if((d = codePointAt(dest, x)) >= 0) {
			return d;
		} else {
			return codePointAt(dest, length(dest) - 1);
		}
	}

	/**
	 * 文字列を入れ替えます。
	 * 
	 * @param source 入替前の文字パターン
	 * @param dest   入替後の文字パターン
	 * @param s      対象の文字列
	 * @return       入れ替えた結果の文字
	 */
	public static String tr(String source, String dest, String s) {
		StringBuffer b = new StringBuffer();
		int c;

		for(int i = 0; i < s.length(); i += c > 0xffff ? 2 : 1) {
			b.appendCodePoint(tr(source, dest, c = s.codePointAt(i)));
		}
		return b.toString();
	}

}
