package online.view.el;

import java.beans.FeatureDescriptor;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

import javax.el.ELContext;
import javax.el.ELResolver;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.PageContext;

import online.view.ViewUtil;
import online.view.model.ViewMap;

/**
 * ELリゾルバ
 *
 * @author Tadashi Nakayama
 */
public class ViewELResolver extends ELResolver {

	/**
	 * @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object)
	 */
	@Override
	public Class<?> getCommonPropertyType(final ELContext context, final Object base) {
		return (base == null) ? Map.class : null;
	}

	/**
	 * @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object)
	 */
	@Override
	public Iterator<FeatureDescriptor> getFeatureDescriptors(
			final ELContext context, final Object base) {
		return Collections.<FeatureDescriptor>emptyList().iterator();
	}

	/**
	 * @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object)
	 */
	@Override
	public Class<?> getType(final ELContext context, final Object base, final Object property) {
		return Object.class;
	}

	/**
	 * @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object)
	 */
	@Override
	public Object getValue(final ELContext context, final Object base, final Object property) {
		if (String.class.isInstance(property)) {
			if (base == null || ViewMap.class.isInstance(base)) {
				return doViewMap(context, base, property);
			} else if (ViewBand.class.isInstance(base)) {
				return doBand(context, base, property);
			}
		}
		return null;
	}

	/**
	 * ViewMap用処理
	 *
	 * @param context ELContext
	 * @param base Object
	 * @param property Object
	 * @return Object
	 */
	private Object doViewMap(final ELContext context, final Object base, final Object property) {
		final var page = PageContext.class.cast(context.getContext(JspContext.class));
		final var map = getViewMap(page, base);
		if (map != null) {
			final var key = String.class.cast(property);
			if ("band".equals(key)) {
				context.setPropertyResolved(true);
				return new ViewBand(map);
			} else if (page.getAttributesScope(key) == 0 || map.containsKey(key)) {
				context.setPropertyResolved(true);
				return map.get(key);
			}
		}
		return null;
	}

	/**
	 * Band用処理
	 *
	 * @param context ELContext
	 * @param base Object
	 * @param property Object
	 * @return Object
	 */
	private Object doBand(final ELContext context, final Object base, final Object property) {
		context.setPropertyResolved(true);
		final var map = ViewBand.class.cast(base).getViewMap();
		if (ViewMap.class.isInstance(map.get(String.class.cast(property)))) {
			return new ViewBand(ViewMap.class.cast(map.get(String.class.cast(property))));
		}
		return new BandList(map, String.class.cast(property));
	}

	/**
	 * ViewMap取得
	 *
	 * @param page PageContext
	 * @param base Baseオブジェクト
	 * @return ViewMap
	 */
	private ViewMap getViewMap(final PageContext page, final Object base) {
		if (page != null) {
			if (base != null) {
				return ViewMap.class.cast(base);
			}
			return ViewMap.class.cast(page.findAttribute(ViewUtil.ATTR_MAP));
		}
		return null;
	}

	/**
	 * @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object)
	 */
	@Override
	public boolean isReadOnly(final ELContext context, final Object base, final Object property) {
		return true;
	}

	/**
	 * @see javax.el.ELResolver
	 * #setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object)
	 */
	@Override
	public void setValue(final ELContext context, final Object base,
			final Object property, final Object value) {
		return;
	}

	/**
	 * バンド
	 *
	 * @author Tadashi Nakayama
	 */
	private static final class ViewBand {
		/** ViewMap */
		private final ViewMap vm;

		/**
		 * コンストラクタ
		 *
		 * @param val ViewMap
		 */
		ViewBand(final ViewMap val) {
			this.vm = val;
		}

		/**
		 * ViewMap取得
		 *
		 * @return ViewMap
		 */
		public ViewMap getViewMap() {
			return this.vm;
		}
	}
}
