package com.tandbergtv.watchpoint.contentmgmt.communication.handlers;

import java.io.File;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

import com.tandbergtv.metadatamanager.factoryImpl.SpecHandlerFactory;
import com.tandbergtv.metadatamanager.spec.ISpecHandler;
import com.tandbergtv.metadatamanager.spec.IValidator;
import com.tandbergtv.metadatamanager.validation.ValidationError;
import com.tandbergtv.watchpoint.communication.Util;
import com.tandbergtv.watchpoint.pmm.communication.HandlerErrorCode;
import com.tandbergtv.watchpoint.pmm.communication.MessageHandler;
import com.tandbergtv.watchpoint.pmm.communication.MessageHandlerException;
import com.tandbergtv.workflow.message.IMessageKey;
import com.tandbergtv.workflow.message.IMessageUID;
import com.tandbergtv.workflow.message.WorkflowMessage;
import com.tandbergtv.workflow.message.WorkflowMessage.MessageType;

/**
 * This message handler is responsible to read the metadata from the given path
 * and validate it against the spec represented by this metadata.
 * 
 * @author spuranik
 * 
 */
public class ValidateMetadataMessageHandler implements MessageHandler {

	/* constants for the message */
	private static String METADATA_FILE_PATH = "filepath";
	private static String VALIDATION_FAILED = "Failed to validate the given metadata: ";
	private static String NO_VALIDATOR_FOR_SPEC = "No validator for spec defined."; 
	
	@Override
	public WorkflowMessage handleMessage(WorkflowMessage message) throws Exception {
		// validate the input.
		Util.validateRequired(message, METADATA_FILE_PATH);

		String metadataFilePath = Util.getStringValueTrimmed(message, METADATA_FILE_PATH);
		Document metadataDOM = createDocument(metadataFilePath);
		ISpecHandler specHandler = SpecHandlerFactory.getInstance(metadataDOM);
		/*
		 * The validator we are actually interested in is called
		 * [specNameAlias]Validator. Can;t depend on the name as its configurable
		 * so pick the first validator registered with the spec handler.
		 */
		IValidator validator = specHandler.getRuleValidators().values().iterator().next();
		/* If no validator was found, throw an exception */
		if(validator == null) {
			throw new MessageHandlerException(HandlerErrorCode.RUNTIME_ERROR, NO_VALIDATOR_FOR_SPEC);
		}
		
		List<ValidationError> errors = validator.validate(metadataDOM);
		
		/* If there were any errors throw an exception */
		if (!errors.isEmpty()) {
			throw new MessageHandlerException(HandlerErrorCode.RUNTIME_ERROR,
					VALIDATION_FAILED + getErrorMsg(errors));
		}

		/* If there were no errors, build the ack response message */
		IMessageUID uid = message.getMessageUID();
		IMessageKey key = message.getKey();
		WorkflowMessage response = new WorkflowMessage(uid, key, MessageType.ack);
		return response;
	}

	/**
	 * Prepares a comma separated list of validation errors.
	 * 
	 * @param errors
	 * @return
	 */
	private String getErrorMsg(List<ValidationError> errors) {
		StringBuilder sb = new StringBuilder();
		for (ValidationError error : errors) {
			if (sb.length() > 0) {
				sb.append(", ");
			}
			sb.append(error.toString());
		}
		return sb.toString();
	}

	/**
	 * Prepares a DOM for the file located at the given filepath.
	 * 
	 * @param filePath
	 * @return
	 */
	private Document createDocument(String filePath) {
		Document document = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd",
							Boolean.FALSE);
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			document = docBuilder.parse(new File(filePath));
		} catch (Exception e) {
			throw new RuntimeException("Unable to parse the metadata file: " + filePath, e);
		}
		return document;
	}

}
