/* ======================================================================
 Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 ====================================================================== */
package org.bodington.spring;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

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

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.bodington.server.BuildingServerException;
import org.bodington.server.resources.Resource;
import org.bodington.util.html.HtmlFilterFactory;
import org.bodington.util.html.nodevisitors.ReportingVisitor;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

/**
 * Generic controller that allows parts of a resource to be edited.
 * 
 * @author buckett
 */
public class ResourceEditController extends SimpleFormController implements
		Validator {
	private static Logger log = Logger.getLogger(ResourceEditController.class);

	private boolean modifyName;

	private boolean modifyTitle;

	private boolean modifyIntroduction;

	private boolean modifyDescription;

	protected Object formBackingObject(HttpServletRequest request)
			throws Exception {
		Resource resource = Utils.getResource(request);
		ResourceEditCommand command = (ResourceEditCommand) createCommand();
		command.setName(resource.getName());
		command.setTitle(resource.getTitle());
		command.setIntroduction(resource.getIntroduction());
		command.setDescription(resource.getDescription());
		command.setResource(resource);
		return command;
	}

	protected ModelAndView onSubmit(HttpServletRequest request,
			HttpServletResponse response, Object object, BindException errors)
			throws Exception {
		Resource resource = Utils.getResource(request);
		ResourceEditCommand command = (ResourceEditCommand) object;
		List sucessMessages = new LinkedList();
		
		if (modifyName && !resource.getName().equals(command.getName())) {
			sucessMessages.add(getMessageSourceAccessor().getMessage(
					"resource.name.updated",
					new Object[] { resource.getName(),
							command.getName() }));
			resource.setName(command.getName());
		}
		
		if (modifyTitle && !resource.getTitle().equals(command.getTitle())) { 
			sucessMessages.add(getMessageSourceAccessor().getMessage("resource.title.updated", new Object[]{resource.getTitle(),command.getTitle()}));
			resource.setTitle(command.getTitle());			
		}
		
		if (modifyDescription && !resource.getDescription().equals(command.getDescription())) {
			sucessMessages.add(getMessageSourceAccessor().getMessage("resource.description.updated"));
			resource.setDescription(command.getDescription());
		}
		
		if (modifyIntroduction && !resource.getIntroduction().equals(command.getIntroduction())) {
		    sucessMessages.add(getMessageSourceAccessor().getMessage("resource.introduction.updated"));
		    resource.setIntroduction(command.getIntroduction());
		}
        
        if (resource.isUnsaved()) {
            resource.save();
            
        }

		return null;

	}

	public static class ResourceEditCommand {

		private Resource resource;

		private String name;

		private String title;

		private String introduction;

		private String description;

		/**
		 * @return the parent
		 */
		public Resource getResource() {
			return resource;
		}

		/**
		 * @param parent
		 *            the parent to set
		 */
		public void setResource(Resource parent) {
			this.resource = parent;
		}

		/**
		 * @return the description
		 */
		public String getDescription() {
			return description;
		}

		/**
		 * @param description
		 *            the description to set
		 */
		public void setDescription(String description) {
			this.description = description;
		}

		/**
		 * @return the introduction
		 */
		public String getIntroduction() {
			return introduction;
		}

		/**
		 * @param introduction
		 *            the introduction to set
		 */
		public void setIntroduction(String introduction) {
			this.introduction = introduction;
		}

		/**
		 * @return the name
		 */
		public String getName() {
			return name;
		}

		/**
		 * @param name
		 *            the name to set
		 */
		public void setName(String name) {
			this.name = name;
		}

		/**
		 * @return the title
		 */
		public String getTitle() {
			return title;
		}

		/**
		 * @param title
		 *            the title to set
		 */
		public void setTitle(String title) {
			this.title = title;
		}
	}

	public boolean supports(Class clazz) {
		return ResourceEditCommand.class.isAssignableFrom(clazz);
	}

	public void validate(Object obj, Errors errors) {
		ResourceEditCommand command = (ResourceEditCommand) obj;

		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "title", "empty",
				"Title cannot be empty");
		ReportingVisitor report = HtmlFilterFactory.filter(HtmlFilterFactory
				.getParser(command.getDescription()),
				HtmlFilterFactory.FRAGMENT);
		List messages = report.getMessages();
		for (Iterator it = messages.iterator(); it.hasNext();) {
			errors.rejectValue("description", "html.invalid", new Object[] { it
					.next() }, "Invalid HTML - {0}");
		}

		report = HtmlFilterFactory.filter(HtmlFilterFactory.getParser(command
				.getIntroduction()), HtmlFilterFactory.BODY);
		messages = report.getMessages();
		for (Iterator it = messages.iterator(); it.hasNext();) {
			errors.rejectValue("introduction", "html.invalid",
					new Object[] { it.next() }, "Invalid HTML - {0}");
		}

		String name = command.getName();
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.empty",
				"Name cannot be empty");

		if (name != null && name.length() > 12) {
			errors.rejectValue("name", "name.too.long", "Name is too long");
		}

		Resource resource = command.getResource();
		if (resource != null && !resource.getName().equals(command.getName())) {
			try {
				if (resource.getParent() != null
						&& resource.getParent().findChild(command.getName()) != null) {
					errors.rejectValue("name", "name.already.exists",
							"Name already exists.");
				}
			} catch (BuildingServerException bse) {
				log.warn("Failed to check for name clash: " + bse.getMessage());
			}
		}

	}

	public boolean isModifyDescription() {
		return modifyDescription;
	}

	public void setModifyDescription(boolean modifyDescription) {
		this.modifyDescription = modifyDescription;
	}

	public boolean isModifyIntroduction() {
		return modifyIntroduction;
	}

	public void setModifyIntroduction(boolean modifyIntroduction) {
		this.modifyIntroduction = modifyIntroduction;
	}

	public boolean isModifyName() {
		return modifyName;
	}

	public void setModifyName(boolean modifyName) {
		this.modifyName = modifyName;
	}

	public boolean isModifyTitle() {
		return modifyTitle;
	}

	public void setModifyTitle(boolean modifyTitle) {
		this.modifyTitle = modifyTitle;
	}

}
