package org.nutz.mvc.init;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

import org.nutz.ioc.Ioc;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.Context;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.ActionInvoker;
import org.nutz.mvc.ActionInvoking;
import org.nutz.mvc.UrlMap;
import org.nutz.mvc.ViewMaker;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Encoding;
import org.nutz.mvc.annotation.Fail;
import org.nutz.mvc.annotation.Filters;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.invoker.ActionInvokerImpl;

public class UrlMapImpl implements UrlMap {

	private static final Log log = Logs.getLog(UrlMapImpl.class);

	private NutConfig config;

	private Ioc ioc;

	private PathNode<ActionInvoker> root;

	private Context context;

	public UrlMapImpl(NutConfig config, Context context, Class<?> mainModule) {
		this.config = config;
		this.ioc = config.getIoc();
		this.root = new PathNode<ActionInvoker>();
		this.context = context;
		this.ok = mainModule.getAnnotation(Ok.class);
		this.fail = mainModule.getAnnotation(Fail.class);
		this.adaptBy = mainModule.getAnnotation(AdaptBy.class);
		this.filters = mainModule.getAnnotation(Filters.class);
		this.encoding = mainModule.getAnnotation(Encoding.class);
	}

	private Ok ok;
	private Fail fail;
	private AdaptBy adaptBy;
	private Filters filters;
	private Encoding encoding;

	public boolean add(List<ViewMaker> makers, Class<?> moduleType) {
		Ok myOk = moduleType.getAnnotation(Ok.class);
		if (null == myOk)
			myOk = ok;
		Fail myFail = moduleType.getAnnotation(Fail.class);
		if (null == myFail)
			myFail = fail;
		AdaptBy myAb = moduleType.getAnnotation(AdaptBy.class);
		if (null == myAb)
			myAb = adaptBy;
		Filters myFlts = moduleType.getAnnotation(Filters.class);
		if (null == myFlts)
			myFlts = filters;
		Encoding myEncoding = moduleType.getAnnotation(Encoding.class);
		if (null == myEncoding)
			myEncoding = encoding;

		// get base url
		At baseAt = moduleType.getAnnotation(At.class);
		String[] bases;
		if (null == baseAt)
			bases = Lang.array("");
		else if (null == baseAt.value() || baseAt.value().length == 0)
			bases = Lang.array("/" + moduleType.getSimpleName().toLowerCase());
		else
			bases = baseAt.value();

		/*
		 * looping all methods in the class, if has one @At, this class will be
		 * take as 'Module'
		 */
		boolean isModule = false;
		for (Method method : moduleType.getMethods()) {
			/*
			 * Make sure the public method, which with @At can be take as the
			 * enter method
			 */
			if (!Modifier.isPublic(method.getModifiers()) || !method.isAnnotationPresent(At.class))
				continue;
			/*
			 * Then, check the @At
			 */
			At atAnn = method.getAnnotation(At.class);
			isModule = true;

			// Create invoker
			ActionInvoker invoker = new ActionInvokerImpl(	context,
															ioc,
															makers,
															moduleType,
															method,
															myOk,
															myFail,
															myAb,
															myFlts,
															myEncoding);

			// Mapping invoker
			String actionPath = null;
			for (String base : bases) {
				String[] paths = atAnn.value();
				// The @At without value
				if ((paths.length == 1 && Strings.isBlank(paths[0])) || paths.length == 0) {
					// Get the action path
					actionPath = base + "/" + method.getName().toLowerCase();
					root.add(actionPath, invoker);

					// Print log
					if (log.isDebugEnabled())
						log.debug(String.format("  %20s() @(%s)", method.getName(), actionPath));
				}
				// More than one value in @At
				else {
					for (String at : paths) {
						// Get Action
						actionPath = base + at;
						root.add(actionPath, invoker);

						// Print log
						if (log.isDebugEnabled())
							log.debug(String.format("  %20s() @(%s)", method.getName(), actionPath));
					}
				}
			}
			// If @At has 'key', it will store the $actionPath to context
			if (!Strings.isBlank(atAnn.key()) && !Strings.isBlank(actionPath))
				config.atMap().add(atAnn.key(), actionPath);
		}
		return isModule;
	}

	public ActionInvoking get(String path) {
		PathInfo<ActionInvoker> info = root.get(path);
		String[] args = Strings.splitIgnoreBlank(info.getRemain(), "[/]");
		return new ActionInvoking(info, args);
	}

}

@@ -2426,16 +2426,18 @@
 %0D%0A%09%09else
+ %7B
 %0D%0A%09%09%09bas
@@ -2457,16 +2457,137 @@
 alue();%0D
+%0A%09%09%09for (int i = 0; i %3C bases.length; i++)%0D%0A%09%09%09%09if (bases%5Bi%5D == null %7C%7C %22/%22.equals(bases%5Bi%5D))%0D%0A%09%09%09%09%09%09bases%5Bi%5D = %22%22;%0D%0A%09%09%7D%0D
 %0A%0D%0A%09%09/*%0D
@@ -3914,20 +3914,11 @@
 %09// 
-More than on
+Hav
 e va

Fix issue 366