/*
 * Copyright 2009 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.lisp.nano.subr;

import java.math.BigInteger;
import java.util.Iterator;

import net.morilib.lisp.nano.ConsIterator;
import net.morilib.lisp.nano.Datum;
import net.morilib.lisp.nano.LispCharacter;
import net.morilib.lisp.nano.LispMessage;
import net.morilib.lisp.nano.LispNumber;
import net.morilib.lisp.nano.LispSmallInt;
import net.morilib.lisp.nano.LispString;
import net.morilib.lisp.nano.util.Iterators;

/**
 * 
 *
 *
 * @author MORIGUCHI, Yuichiro 2009
 */
public final class SubrUtils {

	//
	private SubrUtils() {}

	/**
	 * 
	 * @param n
	 * @param d
	 * @return
	 */
	public static BigInteger ceil(BigInteger n, BigInteger d) {
		BigInteger v = n;

		if(v.compareTo(BigInteger.ZERO) > 0) {
			v = v.add(d);
		}
		return v.divide(d);
	}

	/**
	 * 
	 * @param n
	 * @param d
	 * @return
	 */
	public static BigInteger floor(BigInteger n, BigInteger d) {
		BigInteger v = n;

		if(v.compareTo(BigInteger.ZERO) < 0) {
			v = v.subtract(d);
		}
		return v.divide(d);
	}

	/**
	 * 
	 * @param d
	 * @return
	 */
	public static int getSmallInt(Datum d, LispMessage mesg) {
		if(d instanceof LispSmallInt) {
			LispSmallInt li = (LispSmallInt)d;

			return li.getExactSmallInt();
		} else {
			throw mesg.getError("err.require.smallint", d);
		}
	}

	/**
	 * 
	 * @param d
	 * @return
	 */
	public static int getNonnegativeSmallInt(Datum d,
			LispMessage mesg) {
		if(d instanceof LispSmallInt) {
			LispSmallInt li = (LispSmallInt)d;
			int k = li.getExactSmallInt();

			if(k < 0) {
				throw mesg.getError("err.require.int.nonnegative", d);
			}
			return k;
		} else {
			throw mesg.getError("err.require.int.nonnegative", d);
		}
	}

	/**
	 * 
	 * @param d
	 * @return
	 */
	public static char getCharacter(Datum d, LispMessage mesg) {
		if(d instanceof LispCharacter) {
			return ((LispCharacter)d).getCharacter();
		} else {
			throw mesg.getError("err.require.char", d);
		}
	}

	/**
	 * 
	 * @param d
	 * @return
	 */
	public static int getCharacterCodePoint(Datum d,
			LispMessage mesg) {
		if(d instanceof LispCharacter) {
			return ((LispCharacter)d).getCharacterCodePoint();
		} else {
			throw mesg.getError("err.require.char", d);
		}
	}

	/**
	 * 
	 * @param d
	 * @return
	 */
	public static String getString(Datum d, LispMessage mesg) {
		if(d instanceof LispString) {
			return ((LispString)d).getString();
		} else {
			throw mesg.getError("err.require.string", d);
		}
	}

	/**
	 * 
	 * @param d
	 * @param mesg
	 * @return
	 */
	public static int getU8(Datum d, LispMessage mesg) {
		int c = getSmallInt(d, mesg);

		if(c < 0 || c > 255) {
			throw mesg.getError("err.uvector.outofrange.u8", d);
		}
		return c;
	}

	/**
	 * 
	 * @param itr
	 * @param body
	 * @param mesg
	 */
	public static void checkTerminated(
			ConsIterator itr, Datum body, LispMessage mesg) {
		if(itr.hasNext()) {
			throw mesg.getError("err.argument", body);
		} else if(!itr.getTerminal().isNil()) {
			throw mesg.getError("err.list", body);
		}
	}

	/**
	 * 
	 * @param itr
	 * @param mesg
	 * @param body
	 * @return
	 */
	public static Datum nextIf(Iterator<Datum> itr, LispMessage mesg,
			Datum body) {
		return Iterators.nextIf(itr,
				mesg.getError("err.argument", body));
	}

	/**
	 * 
	 * @param x
	 * @param mesg
	 * @return
	 */
	public static LispNumber getNumber(Datum x, LispMessage mesg) {
		if(x instanceof LispNumber) {
			return (LispNumber)x;
		} else {
			throw mesg.getError("err.require.number", x);
		}
	}

	/**
	 * 
	 * @param ch
	 * @return
	 */
	public static int toFoldCase(int ch) {
		if(ch == 0x130 || ch == 0x131) {  // Turkish i
			return ch;
		}
		return Character.toLowerCase(Character.toUpperCase(ch));
	}

}
