/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * 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 woolpack.fn;

import java.util.Collection;
import java.util.Map;

import woolpack.utils.BuildableHashMap;

/**
 * ユーティリティです。
 * 型推論で表記を簡略するためのスタティックメソッドを含みます。
 * 
 * @author nakamura
 *
 */
public final class FnUtils {
	
	/**
	 * プリミティブ型をオブジェクト型に変換する{@link Fn}です。
	 */
	public static final Fn<Class, Class> TO_WRAPPER = switching(
			new BuildableHashMap<Class, Class>()
			.map(boolean.class, Boolean.class)
			.map(char.class, Character.class)
			.map(byte.class, Byte.class)
			.map(short.class, Short.class)
			.map(int.class, Integer.class)
			.map(long.class, Long.class)
			.map(float.class, Float.class)
			.map(double.class, Double.class), new EchoFn<Class>());
	
	private FnUtils() {
	}
	
	/**
	 * 返却値の型が{@link Void}の{@link NullFn}を返します。
	 * @param <C>
	 * @return 返却値の型が{@link Void}の{@link NullFn}。
	 */
	public static <C> NullFn<C, Void> doVoid() {
		return new NullFn<C, Void>();
	}
	
	public static <R> CastFn<R> castTo(final Class<R> clazz) {
		return new CastFn<R>(clazz);
	}
	
	public static <C, R> ExecFn<C, R> exec(final Fn<? super C, ? extends Fn<? super C, ? extends R>> fn) {
		return new ExecFn<C, R>(fn);
	}
	
	public static <C> EchoFn<C> echo() {
		return new EchoFn<C>();
	}
	
	public static <C, R> FixFn<C, R> fix(final R value) {
		return new FixFn<C, R>(value);
	}
	
	public static <C, R> IfFn<C, R> ifTrue(
			final Fn<? super C, ?> ifFn,
			final Fn<? super C, ? extends R> trueFn,
			final Fn<? super C, ? extends R> falseFn) {
		return new IfFn<C, R>(ifFn, trueFn, falseFn);
	}
	
	public static <A, B, C> JoinFn<A, B, C> join(
			final Fn<? super A, ? extends B> fn0,
			final Fn<? super B, ? extends C> fn1) {
		return new JoinFn<A, B, C>(fn0, fn1);
	}

	public static <A, B, C, D> Fn<A, D> join(
			final Fn<? super A, ? extends B> fn0,
			final Fn<? super B, ? extends C> fn1,
			final Fn<? super C, ? extends D> fn2) {
		return join(join(fn0, fn1), fn2);
	}

	public static <A, B, C, D, E> Fn<A, E> join(
			final Fn<? super A, ? extends B> fn0,
			final Fn<? super B, ? extends C> fn1,
			final Fn<? super C, ? extends D> fn2,
			final Fn<? super D, ? extends E> fn3) {
		return join(join(fn0, fn1), join(fn2, fn3));
	}

	public static <A, B, C, D, E, F> Fn<A, F> join(
			final Fn<? super A, ? extends B> fn0,
			final Fn<? super B, ? extends C> fn1,
			final Fn<? super C, ? extends D> fn2,
			final Fn<? super D, ? extends E> fn3,
			final Fn<? super E, ? extends F> fn4) {
		return join(join(fn0, fn1, fn2), join(fn3, fn4));
	}
	
	public static <C, R> NullFn<C, R> doNull() {
		return new NullFn<C, R>();
	}
	
	public static <C, R> RecodeFn<C, R> recode(
			final Fn<? super C, ? extends R> fn,
			final String name,
			final Collection<String> nameList,
			final Collection<? super C> contextList,
			final Collection<? super R> returnList) {
		return new RecodeFn<C, R>(fn, name, nameList, contextList, returnList);
	}
	
	public static <C, R> RecodeFn<C, R> recode(
			final Fn<? super C, ? extends R> fn,
			final String name,
			final Collection<String> nameList) {
		return new RecodeFn<C, R>(fn, name, nameList);
	}
	
	public static <C, R> RecodeFn<C, R> recode(
			final Fn<? super C, ? extends R> fn,
			final Collection<? super C> contextList,
			final Collection<? super R> returnList) {
		return new RecodeFn<C, R>(fn, contextList, returnList);
	}
	
	public static <C, R> SeqFn<C, R> seq(final Iterable<? extends Fn<? super C, ? extends R>> iterable) {
		return new SeqFn<C, R>(iterable);
	}
	
	public static <C, R> SwitchFn<C, R> switching(
			final Map<? super C, ? extends R> map,
			final Fn<? super C, ? extends R> defaultFn) {
		return new SwitchFn<C, R>(map, defaultFn);
	}
	
	public static <C, R> SwitchFn<C, R> switching(final Map<? super C, ? extends R> map, final R defaultValue) {
		return new SwitchFn<C, R>(map, defaultValue);
	}
	
	public static <C, R> SwitchFn<C, R> switching(
			final Map<? super C, ? extends R> map) {
		return new SwitchFn<C, R>(map);
	}
	
	public static <C, R> ThrowFn<C, R> throwing(final RuntimeException exception) {
		return new ThrowFn<C, R>(exception);
	}
	
	public static <C, R> TryFn<C, R> trying(
			final Fn<? super C, ? extends R> fn,
			final Fn<? super Throwable, ? extends R> reportFn,
			final Fn<? super C, ? extends R> finallyFn) {
		return new TryFn<C, R>(fn, reportFn, finallyFn);
	}
	
	public static <C, R> Delegator<C, R> delegate(
			final Fn<? super C, ? extends R> fn) {
		return new Delegator<C, R>(fn);
	}
	
	public static <C, R> Delegator<C, R> delegate() {
		return new Delegator<C, R>();
	}
}
