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

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import woolpack.el.GettingEL;
import woolpack.fn.Fn;
import woolpack.utils.Utils;
import woolpack.xml.NodeContext;

public class ValueUpdater<E extends Exception> implements Fn<NodeContext, Void, E> {
	private static final String VALUE = "value";
	private static final String SELECTED = "selected";
	private static final String CHECKED = "checked";
	private static final String TRUE = "true";

	private GettingEL valueEL;
	private GettingEL mapEL;
	private boolean selectFlag;
	private final SelectedValueUpdater defaultReturnable;

	public ValueUpdater(
			final GettingEL valueEL,
			final GettingEL mapEL,
			final boolean selectFlag) {
		this.valueEL = valueEL;
		this.mapEL = mapEL;
		this.selectFlag = selectFlag;
		
		defaultReturnable = new SelectedValueUpdater(valueEL, mapEL);
	}
	
	private void executeAttrValue(final NodeContext context, final Element e) {
		final Object value = valueEL.getValue(context);
		if (value != null) {
			e.setAttribute(VALUE, value.toString());
		}
	}
	
	private void executeOption(final NodeContext context, final Element e) {
		if (selectFlag) {
			if (contains(context, e)) {
				e.setAttribute(SELECTED, TRUE);
			} else {
				e.removeAttribute(SELECTED);
			}
		} else {
			executeAttrValue(context, e);
		}
	}
	
	private boolean contains(final NodeContext context, final Element e) {
		final String value = e.getAttribute(VALUE);
		final Iterable<?> iterable = Utils.toIterable(valueEL.getValue(context));
		for (final Object o : iterable) {
			// String型として比較する必要がある。
			if (o.toString().equals(value)) {
				return true;
			}
		}
		return false;
	}

	public Void exec(final NodeContext context) {
		final Element e = (Element) context.getNode();
		final String elementName = e.getNodeName();
		if ("OPTION".equals(elementName)) {
			executeOption(context, e);
		} else if ("SELECT".equals(elementName)) {
			for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
				if (n.getNodeType() == Node.ELEMENT_NODE && "OPTION".equals(n.getNodeName())) {
					executeOption(context, (Element) n);
				}
			}
		} else if ("INPUT".equals(elementName)) {
			final String inputType = e.getAttribute("type");
			if ("file".equals(inputType) || "image".equals(inputType)) {
				// nothing to do.
			} else if (selectFlag) {
				if ("radio".equals(inputType) || "checkbox".equals(inputType)) {
					if (contains(context, e)) {
						e.setAttribute(CHECKED, TRUE);
					} else {
						if (e.hasAttribute(CHECKED)) {
							e.removeAttribute(CHECKED);
						}
					}
				} else {
					executeAttrValue(context, e);
				}
			} else {
				executeAttrValue(context, e);
			}
		} else if ("BUTTON".equals(elementName)) {
			executeAttrValue(context, e);
		} else {
			defaultReturnable.exec(context);
		}
		return null;
	}

	public GettingEL getMapEL() {
		return mapEL;
	}
	public void setMapEL(final GettingEL mapEL) {
		this.mapEL = mapEL;
		defaultReturnable.setValueEL(mapEL);
	}
	public boolean isSelectFlag() {
		return selectFlag;
	}
	public void setSelectFlag(final boolean selectFlag) {
		this.selectFlag = selectFlag;
	}
	public GettingEL getValueEL() {
		return valueEL;
	}
	public void setValueEL(final GettingEL valueEL) {
		this.valueEL = valueEL;
		defaultReturnable.setValueEL(valueEL);
	}
}
