package com.lmscompany.lms.server;

import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.isomorphic.datasource.BasicDataSource;
import com.isomorphic.datasource.DSRequest;
import com.isomorphic.datasource.DSResponse;
import com.isomorphic.datasource.DataSource;
import com.isomorphic.rpc.BaseResponse;
import com.isomorphic.rpc.RPCManager;
import com.isomorphic.servlet.IDACall;
import com.isomorphic.servlet.RequestContext;
import com.lmscompany.lms.server.util.User;

public class LMSIDACall extends IDACall {
	private static final long serialVersionUID = 4543651445782711736L;

	/*
	 * See http://www.smartclient.com/smartgwtee/javadoc/com/smartgwt/client/docs/serverds/OperationBinding.html#requiresRole
	 */
	@Override
	public void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Long userId = User.getUserId(request);
		if (userId != null) {
			try {
				RequestContext context = RequestContext.instance(this, request, response);
				RPCManager rpc = new RPCManager(request, response);
				rpc.setUserId(userId.toString());
				// rpc.setAuthenticated(true);
				// rpc.setUserRoles((String) roles);

				// call processRPCTransaction() to iterate through all RPCRequests and
				// DSRequests and execute them
				processRPCTransaction(rpc, context);

			} catch (Throwable e) {
				handleError(response, e);
			}
		}
	}

	@Override
	public DSResponse handleDSRequest(DSRequest dsRequest, RPCManager rpc, RequestContext context) throws Exception {
		String allowedClientOperationTypesProperty = ((BasicDataSource) dsRequest.getDataSource())
				.getProperty("allowedClientOperationTypes");
		if (allowedClientOperationTypesProperty != null) {
			List<String> allowedClientOperationTypesList = new LinkedList<String>(Arrays.asList(allowedClientOperationTypesProperty
					.split(" ")));
			if (!allowedClientOperationTypesList.contains(dsRequest.getOperationType()))
				if (dsRequest.getOperationType() != null) {
					switch (dsRequest.getOperationType()) {
					case DataSource.OP_ADD:
					case DataSource.OP_CLIENT_EXPORT:
					case DataSource.OP_CUSTOM:
					case DataSource.OP_FETCH:
					case DataSource.OP_REMOVE:
					case DataSource.OP_UPDATE:
						return new DSResponse().setDataSource(dsRequest.getDataSource()).setOperationType(dsRequest.getOperationType())
								.setFailure("OperationType \"" + dsRequest.getOperationType() + "\" not allowed");
					case DataSource.OP_VALIDATE:
						return new DSResponse().setDataSource(dsRequest.getDataSource()).setOperationType(dsRequest.getOperationType())
								.setStatus(BaseResponse.STATUS_VALIDATION_ERROR);
					default:
						return new DSResponse().setDataSource(dsRequest.getDataSource()).setFailure(
								"The given OperationType is not allowed");
					}
				} else
					return new DSResponse().setDataSource(dsRequest.getDataSource()).setFailure(
							"No OperationType was given for the request");
		}
		return super.handleDSRequest(dsRequest, rpc, context);
	}

	/*
	 * This code is not needed as it only affects Client-side requests. In order to also add Criteria
	 * to server requests, adding criteria is now done inside of LMSSQLDataSource, referenced from ALL .ds.xml files
	@Override
	public DSResponse handleDSRequest(DSRequest arg0, RPCManager arg1, RequestContext arg2) throws Exception {
		// Add "TENANT_ID = xxx" - Value for add
		if (arg0.getOperationType().equals(DataSource.OP_ADD)) {
			@SuppressWarnings("unchecked")
			Map<String, Object> requestValues = arg0.getValues();
			requestValues.put("TENANT_ID", User.getUserTenantId(arg0.getHttpServletRequest()));
			arg0.setValues(requestValues);
		} else
		// Add "TENANT_ID = x" - Criteria for fetch/update/remove
		if (arg0.getOperationType().equals(DataSource.OP_FETCH) || arg0.getOperationType().equals(DataSource.OP_UPDATE)
				|| arg0.getOperationType().equals(DataSource.OP_REMOVE)) {
			arg0.addToCriteria("TENANT_ID", DefaultOperators.Equals, User.getUserTenantId(arg0.getHttpServletRequest()));
			// Add additional "xyz_TENANT_ID = x" - Criteria for fetch for
			// "includeFrom"-fields
			if (arg0.getOperationType().equals(DataSource.OP_FETCH)) {
				for (Object fn : arg0.getDataSource().getFieldNames()) {
					String fieldName = fn.toString();
					if (fieldName.endsWith("_TENANT_ID")) {
						if (!isOuterJoined(fieldName, arg0.getDataSource()))
							arg0.addToCriteria(fieldName, DefaultOperators.Equals, User.getUserTenantId(arg0.getHttpServletRequest()));
						else {
							// xyz_TENANT_ID = x OR xyz_TENANT_ID IS NULL
							AdvancedCriteria xyzTenantID = new AdvancedCriteria(DefaultOperators.Or, new Criterion[] {
									new SimpleCriterion(fieldName, DefaultOperators.Equals, User.getUserTenantId(arg0
											.getHttpServletRequest())), new IsNullCriterion(fieldName) });
							// (old == null) -> new : (old)+(new)
							AdvancedCriteria newAc = (arg0.getAdvancedCriteria() == null) ? xyzTenantID : new AdvancedCriteria(
									DefaultOperators.And, new Criterion[] { arg0.getAdvancedCriteria().asCriterion(),
											xyzTenantID.asCriterion() });
							arg0.setAdvancedCriteria(newAc);
						}
					}
				}
			}
		}
		return super.handleDSRequest(arg0, arg1, arg2);
	}

	private static boolean isOuterJoined(String includingField, DataSource ds) {
		DSField includingFieldDSField = ds.getField(includingField);
		String includeFromString = includingFieldDSField.getProperty("includeFrom");

		String includeFromTable = (includeFromString == null || includeFromString.indexOf(".") == -1) ? null
				: includeFromString.substring(0, includeFromString.indexOf("."));

		if (includeFromTable != null)
			for (Object fn : ds.getFieldNames()) {
				String fieldName = fn.toString();
				DSField dsField = ds.getField(fieldName);
				if (dsField.getProperty("foreignKey") != null
						&& dsField.getProperty("foreignKey").startsWith(includeFromTable + "."))
					return JoinType.OUTER.getValue().equals(dsField.getProperty("joinType"));
			}
		return false;
	}
	*/
}