/*
 * 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.visitor.impl;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

import woolpack.locale.BranchByLocaleValidator;
import woolpack.text.DelegateDateFormat;
import woolpack.text.DelegateNumberFormat;
import woolpack.text.FormatFactory;
import woolpack.text.LimitedValueFormat;
import woolpack.text.RegExpFormat;
import woolpack.text.ToCharacterFormat;
import woolpack.text.ToStringFormat;
import woolpack.utils.MapBuilder;
import woolpack.utils.Switchable;
import woolpack.validator.CountValidator;
import woolpack.validator.DelegationValidator;
import woolpack.validator.DumpValidator;
import woolpack.validator.IfNotValidator;
import woolpack.validator.IfValidator;
import woolpack.validator.ValidatorConstants;
import woolpack.validator.ValidatorExpression;
import woolpack.visitor.Acceptable;
import woolpack.visitor.JsVisitorConstants;
import woolpack.visitor.Visitor;

/**
 * コンストラクタツリーを JS コンストラクタツリーに変換するための 定数の集まり。
 * 
 * @author nakamura
 * 
 */
public final class JsConstants {

	/**
	 * {@link BranchByLocaleValidator}を JS コンストラクタ宣言に変換する{@link Acceptable}。
	 */
	public static final Acceptable<BranchByLocaleValidator>
	ACCEPT_BRANCH_BY_LOCALE_VALIDATOR = JsVisitorConstants
			.classNameAcceptable(new Acceptable<BranchByLocaleValidator>() {
				public void accept(final Visitor visitor,
						final BranchByLocaleValidator v) {
					visitor.visit(v.getSwitchable());
				}
			});

	/**
	 * {@link IfNotValidator}を JS コンストラクタ宣言に変換する{@link Acceptable}。
	 */
	public static final Acceptable<IfNotValidator> ACCEPT_IF_NOT_VALIDATOR
	= new Acceptable<IfNotValidator>() {
		public void accept(final Visitor visitor, final IfNotValidator v) {
			final StringBuilder sb = (StringBuilder) visitor.getContext();
			sb.append("new IfNotValidator(");
			visitor.visit(v.getIfExpression());
			sb.append(',');
			visitor.visit(v.getFalseExpression());
			sb.append(')');
		}
	};

	/**
	 * {@link IfValidator}を JS コンストラクタ宣言に変換する{@link Acceptable}。
	 */
	public static final Acceptable<IfValidator> ACCEPT_IF_VALIDATOR
	= new Acceptable<IfValidator>() {
		public void accept(final Visitor visitor, final IfValidator v) {
			final StringBuilder sb = (StringBuilder) visitor.getContext();
			sb.append("new IfValidator(");
			visitor.visit(v.getIfExpression());
			sb.append(',');
			visitor.visit(v.getTrueExpression());
			sb.append(',');
			visitor.visit(v.getFalseExpression());
			sb.append(')');
		}
	};

	/**
	 * {@link SimpleDateFormat}を JS コンストラクタ宣言に変換する{@link Acceptable}。
	 */
	public static final Acceptable<SimpleDateFormat> ACCEPT_SIMPLE_DATE_FORMAT
	= new Acceptable<SimpleDateFormat>() {
		public void accept(final Visitor visitor, final SimpleDateFormat v) {
			final StringBuilder sb = (StringBuilder) visitor.getContext();
			sb.append("new SimpleDateFormat(");
			visitor.visit(v.toPattern());
			sb.append(')');
		}
	};

	/**
	 * {@link wookpack.validator}パッケージで定義されているクラスに関する コンストラクタツリーを JS
	 * コンストラクタツリーに変換するためのに使用する{@link Map}。 {@link Switchable#getDefault()}を無視する。
	 */
	public static final Map<Object, Acceptable> BASE = Collections
			.unmodifiableMap(MapBuilder
					.get(new LinkedHashMap<Object, Acceptable>())
					.put(
							ValidatorConstants.TRUE,
							JsVisitorConstants
									.objectAcceptable("new TrueValidator()"))
					.put(
							ValidatorConstants.FALSE,
							JsVisitorConstants
									.objectAcceptable("new FalseValidator()"))
					.put(
							ValidatorConstants.REQUIRED,
							JsVisitorConstants.objectAcceptable(
									"new RequiredValidator()"))
					.put(
							ValidatorConstants.OR,
							JsVisitorConstants.objectAcceptable(
									"new OrValidatorIterable()"))
					.put(
							ValidatorConstants.OROR,
							JsVisitorConstants.objectAcceptable(
									"new OrOrValidatorIterable()"))
					.put(
							ValidatorConstants.AND,
							JsVisitorConstants.objectAcceptable(
									"new AndValidatorIterable()"))
					.put(
							ValidatorConstants.ANDAND,
							JsVisitorConstants.objectAcceptable(
									"new AndAndValidatorIterable()"))
					.put(
							ValidatorConstants.EQ,
							JsVisitorConstants.objectAcceptable(
									"new EqValidatorIterable()"))
					.put(
							ValidatorConstants.EQEQ,
							JsVisitorConstants.objectAcceptable(
									"new EqEqValidatorIterable()"))
					.put(CountValidator.class,
							IgnoreValidatorConstants.IGNORE_COUNT_VALIDATOR)
					.put(DumpValidator.class,
							IgnoreValidatorConstants.IGNORE_DUMP_VALIDATOR)
					.put(BranchByLocaleValidator.class,
							ACCEPT_BRANCH_BY_LOCALE_VALIDATOR)
					.put(IfNotValidator.class, ACCEPT_IF_NOT_VALIDATOR)
					.put(IfValidator.class, ACCEPT_IF_VALIDATOR)
					.put(
							ValidatorExpression.class,
							JsVisitorConstants.classNameAcceptable(
									JsVisitorConstants.PARAMS))
					.get());

	/**
	 * 値検証/値変換に関する コンストラクタツリーを JS コンストラクタツリーに変換するためのに使用する{@link Map}。
	 * 
	 * @see #BASE
	 */
	public static final Map<Object, Acceptable> MAP = Collections
			.unmodifiableMap(MapBuilder
					.get(new LinkedHashMap<Object, Acceptable>())
					.putAll(BASE)
					.put(DelegationValidator.class,
							IgnoreValidatorConstants
							.IGNORE_DELEGATION_VALIDATOR)

					.put(Iterable.class, JsVisitorConstants.ITERABLE)
					.put(Switchable.class,
							JsVisitorConstants.switchableAcceptable(null))
					.put(Map.class, JsVisitorConstants.MAP)
					.put(FormatFactory.class,
							IgnoreTextConstants.IGNORE_FORMAT_FACTORY)

					.put(
							LimitedValueFormat.class,
							JsVisitorConstants.classNameAcceptable(
									JsVisitorConstants.PARAMS))
					.put(
							RegExpFormat.class,
							JsVisitorConstants.classNameAcceptable(
									JsVisitorConstants.PARAMS))
					.put(
							ToCharacterFormat.class,
							JsVisitorConstants.objectAcceptable(
									"new ToCharacterFormat()"))
					.put(
							ToStringFormat.class,
							JsVisitorConstants.objectAcceptable(
									"new ToStringFormat()"))

					.put(DelegateNumberFormat.class,
							IgnoreTextConstants.IGNORE_DELEGATE_NUMBER_FORMAT)
					.put(
							DecimalFormat.class,
							JsVisitorConstants.objectAcceptable(
									"new DecimalFormat()"))

					.put(DelegateDateFormat.class,
							IgnoreTextConstants.IGNORE_DELEGATE_DATE_FORMAT)
					.put(SimpleDateFormat.class, ACCEPT_SIMPLE_DATE_FORMAT)
					.put(
							DateFormat.class,
							JsVisitorConstants
									.objectAcceptable("new SimpleDateFormat"
											+ "(\"yyyyMMdd\")"))

					.put(
							String.class,
							JsVisitorConstants.escapeStringAcceptable(
									JsVisitorConstants.OBJECT))
					.put(Locale.class).put(Pattern.class).put(Object.class,
							JsVisitorConstants.OBJECT).get());

	private JsConstants() {
	} // カバレージがここを通過してはいけない
}
