/* ValidationMessagesELResolver.java

	Purpose:
		
	Description:
		
	History:
		Mary 29, 2012 3:18:34 PM, Created by dennis

Copyright (C) 2012 Potix Corporation. All Rights Reserved.
*/

package org.zkoss.bind.xel.zel;

import java.beans.FeatureDescriptor;
import java.io.Serializable;
import java.util.Iterator;

import org.zkoss.bind.sys.ValidationMessages;
import org.zkoss.zel.ELContext;
import org.zkoss.zel.ELException;
import org.zkoss.zel.ELResolver;
import org.zkoss.zel.PropertyNotFoundException;
import org.zkoss.zel.PropertyNotWritableException;
import org.zkoss.zk.ui.Component;

/**
 * ELResolver for {@link ValidationMessages}. It doesn't handle vmsgs.texts[...]. To use vmsgs.texts[...] you could use 
 * <code>org.zkoss.zkmax.bind.impl.ValidationMessageELResolver</code>
 * @author dennis
 * @since 6.0.1
 */
public class ValidationMessagesELResolver extends ELResolver {

	public Object getValue(ELContext context, Object base, Object property)
			throws NullPointerException, PropertyNotFoundException, ELException {
		if (context == null) {
			throw new NullPointerException();
		}
		if (base == null)
			return null;

		if (base instanceof ValidationMessages) {
			final int numOfKids = ((Integer) context.getContext(Integer.class)).intValue(); //get numOfKids, see #PathResolver

			final ValidationMessages vms = (ValidationMessages) base;
			String[] msgs = null;

			if (property instanceof Component) {
				context.setPropertyResolved(true);
				if (numOfKids == 0) { //case vmsgs[tb1]
					msgs = vms.getMessages((Component) property);
					if (msgs == null || msgs.length == 0)
						return null;
					return msgs[0];
				} else { //case vmsgs[tb1][*]
					return new ComponentResolver((ValidationMessages) base, (Component) property);
				}
			} else if (property instanceof String) {
				context.setPropertyResolved(true);
				if ("texts".equals(property)) {
					return handleTexts((ValidationMessages) base, numOfKids);
				} else { //case vmsgs['key']
					msgs = vms.getKeyMessages((String) property);
					if (msgs == null || msgs.length == 0)
						return null;
					return msgs[0];
				}
			}
		} else if (base instanceof ValueResolver) {
			final int numOfKids = ((Integer) context.getContext(Integer.class)).intValue(); //get numOfKids, see #PathResolver
			return ((ValueResolver) base).getValue(context, property, numOfKids);
		}

		return null;
	}

	public Class<?> getType(ELContext context, Object base, Object property)
			throws NullPointerException, PropertyNotFoundException, ELException {
		//get type is called by setValue,see AstValue#setValue, 
		//since this is ready only resolver, we don't need to implement it.
		return null;
	}

	public void setValue(ELContext context, Object base, Object property, Object value)
			throws NullPointerException, PropertyNotFoundException, PropertyNotWritableException, ELException {
	}

	public boolean isReadOnly(ELContext context, Object base, Object property)
			throws NullPointerException, PropertyNotFoundException, ELException {
		return true;
	}

	public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
		return null;
	}

	public Class<?> getCommonPropertyType(ELContext context, Object base) {
		return null;
	}

	protected static interface ValueResolver {
		public Object getValue(ELContext context, Object property, int numOfKids);
	}

	protected abstract static class AbstractValueResolver implements Serializable, ValueResolver {
		private static final long serialVersionUID = 1L;
		protected final ValidationMessages _vms;

		protected AbstractValueResolver(ValidationMessages vms) {
			this._vms = vms;
		}
	}

	//vmsgs[tb1]*
	private static class ComponentResolver extends AbstractValueResolver {
		private static final long serialVersionUID = 1L;
		protected final Component _comp;

		ComponentResolver(ValidationMessages vms, Component comp) {
			super(vms);
			this._comp = comp;
		}

		public Object getValue(ELContext context, Object property, int numOfKids) {
			String[] msgs = null;
			if (property instanceof String) { //vmsgs[tb1]['attr']
				context.setPropertyResolved(true);
				msgs = _vms.getMessages(_comp, (String) property);
				if (msgs != null && msgs.length > 0) {
					return msgs[0];
				}
				return null;
			} else {
				throw new PropertyNotFoundException("uknow property " + property + " for validation-messages[comp][*]");
			}
		}
	}

	protected Object handleTexts(ValidationMessages base, int numOfKids) {
		if (numOfKids == 0) { //case vmsgs.texts
			return base.getMessages();
		} else { //case vmsgs.texts[*]
			return new TextsResolver((ValidationMessages) base);
		}
	}

	//vmsgs.texts.*
	private static class TextsResolver extends AbstractValueResolver {
		private static final long serialVersionUID = 1L;

		TextsResolver(ValidationMessages vms) {
			super(vms);
		}

		public Object getValue(ELContext context, Object property, int numOfKids) {
			String[] msgs = null;
			if (property instanceof Component) {
				context.setPropertyResolved(true);
				if (numOfKids == 0) { //case vmsgs.texts[tb1]
					return _vms.getMessages((Component) property);
				} else { //case vmsgs.texts[tb1]*
					return new TextsComponentResolver(_vms, (Component) property);
				}
			} else if (property instanceof String) { //case vmsgs.texts['key']
				context.setPropertyResolved(true);
				return _vms.getKeyMessages((String) property);
			} else if (property instanceof Number) { //case vmsgs.texts[index]
				context.setPropertyResolved(true);
				msgs = _vms.getMessages();
				return msgs.length == 0 ? null : msgs[((Number) property).intValue()];
			}
			return null;
		}
	}

	//vmsgs.texts[tb1]*
	private static class TextsComponentResolver extends AbstractValueResolver {
		private static final long serialVersionUID = 1L;
		protected final Component _comp;

		TextsComponentResolver(ValidationMessages vms, Component comp) {
			super(vms);
			this._comp = comp;
		}

		public Object getValue(ELContext context, Object property, int numOfKids) {
			String[] msgs = null;
			if (property instanceof String) { //vmsgs.texts[tb1]['attr']
				context.setPropertyResolved(true);
				return _vms.getMessages(_comp, (String) property);
			} else if (property instanceof Number) { //vmsgs.texts[tb1][index]
				context.setPropertyResolved(true);
				msgs = _vms.getMessages(_comp);
				return msgs.length == 0 ? null : msgs[((Number) property).intValue()];
			}
			return null;
		}
	}
}
