package net.wicp.tams.duckula.ops.services;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;

import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.commons.Configuration;
import org.apache.tapestry5.commons.MappedConfiguration;
import org.apache.tapestry5.commons.OrderedConfiguration;
import org.apache.tapestry5.http.services.Request;
import org.apache.tapestry5.http.services.RequestFilter;
import org.apache.tapestry5.http.services.RequestHandler;
import org.apache.tapestry5.http.services.Response;
import org.apache.tapestry5.http.services.Session;
import org.apache.tapestry5.ioc.ServiceBinder;
import org.apache.tapestry5.ioc.annotations.Contribute;
import org.apache.tapestry5.ioc.annotations.Local;
import org.apache.tapestry5.ioc.services.ApplicationDefaults;
import org.apache.tapestry5.ioc.services.SymbolProvider;
import org.slf4j.Logger;

import net.wicp.tams.cas.SymbolConstantsCas;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.jdbc.service.IProParam;
import net.wicp.tams.common.thread.threadlocal.PerThreadValue;
import net.wicp.tams.common.thread.threadlocal.PerthreadManager;
import net.wicp.tams.duckula.ops.ajax.AjaxImpl;
import net.wicp.tams.duckula.ops.ajax.IAjax;
import net.wicp.tams.duckula.ops.beans.SessionBean;

/**
 * This module is automatically included as part of the Tapestry IoC Registry,
 * it's a good place to configure and extend Tapestry, or to place your own
 * service definitions.
 */
public class AppModule {
	public static void bind(ServiceBinder binder) {
		// binder.bind(MyServiceInterface.class, MyServiceImpl.class);

		// Make bind() calls on the binder object to define most IoC services.
		// Use service builder methods (example below) when the implementation
		// is provided inline, or requires more initialization than simply
		// invoking the constructor.
		binder.bind(IAjax.class, AjaxImpl.class);
	}

	public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration) {
		// The values defined here (as factory default overrides) are themselves
		// overridden with application defaults by DevelopmentModule and QaModule.

		// The application version is primarily useful as it appears in
		// any exception reports (HTML or textual).
		configuration.override(SymbolConstants.APPLICATION_VERSION, "1.0");

		// This is something that should be removed when going to production, but is
		// useful
		// in the early stages of development.
		configuration.override(SymbolConstants.PRODUCTION_MODE, false);
	}

	public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration) {
		// Contributions to ApplicationDefaults will override any contributions to
		// FactoryDefaults (with the same key). Here we're restricting the supported
		// locales to just "en" (English). As you add localised message catalogs and
		// other assets,
		// you can extend this list of locales (it's a comma separated series of locale
		// names;
		// the first locale name is the default when there's no reasonable match).
		configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en,zh");

		// You should change the passphrase immediately; the HMAC passphrase is used to
		// secure
		// the hidden field data stored in forms to encrypt and digitally sign
		// client-side data.
		configuration.add(SymbolConstants.HMAC_PASSPHRASE, "change this immediately-" + UUID.randomUUID());

		//configuration.add(SymbolConstantsCas.conf_cas_db, "duckula");// 示例，需要配置全局db
	}

	/**
	 * Use annotation or method naming convention:
	 * <code>contributeApplicationDefaults</code>
	 */
	@Contribute(SymbolProvider.class)
	@ApplicationDefaults
	public static void setupEnvironment(MappedConfiguration<String, Object> configuration) {
		// Support for jQuery is new in Tapestry 5.4 and will become the only supported
		// option in 5.5.
		configuration.add(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER, "jquery");
		configuration.add(SymbolConstants.BOOTSTRAP_ROOT, "context:mybootstrap");
	}

	/**
	 * This is a service definition, the service will be named "TimingFilter". The
	 * interface, RequestFilter, is used within the RequestHandler service pipeline,
	 * which is built from the RequestHandler service configuration. Tapestry IoC is
	 * responsible for passing in an appropriate Logger instance. Requests for
	 * static resources are handled at a higher level, so this filter will only be
	 * invoked for Tapestry related requests.
	 *
	 *
	 * Service builder methods are useful when the implementation is inline as an
	 * inner class (as here) or require some other kind of special initialization.
	 * In most cases, use the static bind() method instead.
	 *
	 *
	 * If this method was named "build", then the service id would be taken from the
	 * service interface and would be "RequestFilter". Since Tapestry already
	 * defines a service named "RequestFilter" we use an explicit service id that we
	 * can reference inside the contribution method.
	 */
	public RequestFilter buildTimingFilter(final Logger log) {
		return new RequestFilter() {
			public boolean service(Request request, Response response, RequestHandler handler) throws IOException {
				// 是否存在session

				PerThreadValue<Boolean> sessionstatusValue = PerthreadManager.getInstance()
						.createValue("tams:sessionstatus", Boolean.class);
				Session session = request.getSession(false);

				if (session != null) {
					// 把租户ID放到线程上下文中
					SessionBean sessionBean = (SessionBean) session
							.getAttribute("sso:net.wicp.tams.duckula.ops.beans.SessionBean");
					if (sessionBean != null) {
						sessionstatusValue.set(true);
						// 是不是超级管理员
						PerThreadValue<Boolean> superUserValue = PerthreadManager.getInstance()
								.createValue(Conf.get("common.jdbc.sqlIntercept.superUser"), Boolean.class);
						superUserValue.set(sessionBean.isSuperUser());
						Map<String, String> colnameMapAll = IProParam.getColnameMapAll();
						if (colnameMapAll.containsKey("tenant_id")) {
							Long tenantId = sessionBean.getTenantId();
							// 删除，新增都需要这个字段，但它的key是一样的：tams:sqlIntercept:tenant_id
							PerThreadValue<String> tenantValue = PerthreadManager.getInstance()
									.createValue(colnameMapAll.get("tenant_id"), String.class);
							tenantValue.set(String.valueOf(tenantId));// 去掉0，如果不区分租户则去掉tenantid字段
						}

						if (colnameMapAll.containsKey("update_by")) {
							Long userId = sessionBean.getSysUser().getId();
							// 删除，新增都需要这个字段，但它的key是一样的：tams:sqlIntercept:tenant_id
							PerThreadValue<String> tenantValue = PerthreadManager.getInstance()
									.createValue(colnameMapAll.get("update_by"), String.class);
							tenantValue.set(String.valueOf(userId));// 0为公共资源，可以查看，但不能修改、删除
						}

						if (colnameMapAll.containsKey("create_by")) {
							Long userId = sessionBean.getSysUser().getId();
							// 删除，新增都需要这个字段，但它的key是一样的：tams:sqlIntercept:tenant_id
							PerThreadValue<String> tenantValue = PerthreadManager.getInstance()
									.createValue(colnameMapAll.get("create_by"), String.class);
							tenantValue.set(String.valueOf(userId));// 0为公共资源，可以查看，但不能修改、删除
						}

					} else {
						sessionstatusValue.set(false);
					}
				} else {
					sessionstatusValue.set(false);
				}

				long startTime = System.currentTimeMillis();

				try {
					// The responsibility of a filter is to invoke the corresponding method
					// in the handler. When you chain multiple filters together, each filter
					// received a handler that is a bridge to the next filter.

					return handler.service(request, response);
				} finally {
					long elapsed = System.currentTimeMillis() - startTime;

					log.info("Request time: {} ms", elapsed);
					// 在这里清险上下文，否则会出现在一个调用中一个SQL拦截成功，清除后，另一个拦截失败的问题。
					Map<String, String> pre = Conf.getPre("common.jdbc.sqlIntercept", false);
					for (String key : pre.keySet()) {
						PerthreadManager.getInstance().cleanValue(Conf.get(key));
					}
					PerthreadManager.getInstance().cleanValue("tams:sessionstatus");
				}
			}
		};
	}

	/**
	 * This is a contribution to the RequestHandler service configuration. This is
	 * how we extend Tapestry using the timing filter. A common use for this kind of
	 * filter is transaction management or security. The @Local annotation selects
	 * the desired service by type, but only from the same module. Without @Local,
	 * there would be an error due to the other service(s) that implement
	 * RequestFilter (defined in other modules).
	 */
	@Contribute(RequestHandler.class)
	public void addTimingFilter(OrderedConfiguration<RequestFilter> configuration, @Local RequestFilter filter) {
		// Each contribution to an ordered configuration has a name, When necessary, you
		// may
		// set constraints to precisely control the invocation order of the contributed
		// filter
		// within the pipeline.

		configuration.add("Timing", filter);
	}

	public static void contributeIgnoredPathsFilter(Configuration<String> configuration) {
		configuration.add("/connector");
		configuration.add("/websocket");
	}

//	/***
//	 * 安装配置
//	 * 
//	 * @param configuration
//	 */
//	@Contribute(WebSecurityManager.class)
//	public static void addRealms(Configuration<Realm> configuration) {
//		ExtendedPropertiesRealm realm = new ExtendedPropertiesRealm("classpath:shiro-users.properties");
//		configuration.add(realm);
//	}
}
