package com.mozartframework.mozart.producer;

import com.mozartframework.logger.TLogger;
import com.mozartframework.mozart.Defaults;
import com.mozartframework.mozart.framework.DocumentInfo;
import com.mozartframework.mozart.framework.Page;
import com.mozartframework.mozart.framework.RequestContext;
import com.mozartframework.util.ErrorCodeException;
import com.mozartframework.util.InputOutput;
import com.mozartframework.io.UnknownIOSourceException;
import com.mozartframework.xml.parser.IncludableParser;
import com.mozartframework.xml.parser.XmlDoc;
import com.mozartframework.xml.parser.Parser;
import org.w3c.dom.Element;
import static com.mozartframework.mozart.producer.MetaFileType.*;

import java.util.HashSet;
import java.util.Set;

/**
 * Создает объект {@link Page} на основе информации, содержащейся в {@link com.mozartframework.mozart.framework.RequestContext}.<br/>
 * @version $Revision: 1.25 $
 */

public class ProducerFromFile implements Producer {
	
	private TLogger logger = new TLogger(ProducerFromFile.class);

	/**
	 * Создает объект {@link Page} на основе информации, содержащейся в {@link com.mozartframework.mozart.framework.RequestContext}.<br/>
	 * После чего используя метод {@link com.mozartframework.mozart.framework.RequestContext#setPage(Page)}, устанавливает
	 * созданный {@link Page} в {@link com.mozartframework.mozart.framework.RequestContext}.
	 * <P>Для создания <code>DOM Document</code>, являющегося частью {@link Page}, используется
	 * <code>DOM</code> парсер {@link com.mozartframework.xml.parser.IncludableParser} и
	 * {@link com.mozartframework.cache.Cache} зарегестрированный в {@link com.mozartframework.mozart.framework.HostContext}
	 * с именем сегмента "xml".
	 *
	 * @see com.mozartframework.mozart.framework.RequestContext
	 * @see com.mozartframework.mozart.framework.Page
	 * @throws com.mozartframework.util.ErrorCodeException
	 */
	public void produce(RequestContext context) throws ErrorCodeException {
		try {
			InputOutput ioFile = getIO(context, context.getInfo().getSrc());
			boolean validate = context.getInfo().isValidate();
			
			Page page;

			Parser p = getParser(context);
			XmlDoc xmlDoc = p.parse(ioFile, validate);

			Set<String> includedMataFiles = new HashSet<String>();
			DocumentInfo info = context.getInfo();

			includeMetaFile(xmlDoc, context, SELF, includedMataFiles, info, validate);
			includeMetaFile(xmlDoc, context, PARENT, includedMataFiles, info, validate);
			includeMetaFile(xmlDoc, context, GROUP, includedMataFiles, info, validate);
			includeMetaFile(xmlDoc, context, ROOT, includedMataFiles, info, validate);
			includeMetaFile(xmlDoc, context, GLOBAL, includedMataFiles, info, validate);

			page = new Page(xmlDoc);
			context.setPage(page);
		}
		catch (Exception e) {
			throw new ErrorCodeException(e);
		}
	}

	/**
	 *
	 */
	private InputOutput getIO(RequestContext context, String src) throws UnknownIOSourceException {
		String uri = (src.indexOf("://") > 0) ? src : Defaults.PREFIX_SRC_NAME + "://" + src;
		return context.createInputOutput(uri);
	}


	/**
	 * Вставляет мета файл в конец XMLDOC, если он существует на диске
	 * @param xmlDoc - куда вставляем
	 * @param context - request context
	 * @param meta - ключ переменной в Info RequestContext
	 */
	private void includeMetaFile( XmlDoc xmlDoc, RequestContext context, MetaFileType metaFileType,	Set<String> includedMataFiles,
			DocumentInfo info, boolean validate) throws Exception {

		String metaSrc = findMetaFileSrc(metaFileType, info);
		if (metaSrc != null) {
			try {
				if (includedMataFiles.contains(metaSrc)) {
					return; // уже включили данный meta файл
				}else{
					includedMataFiles.add(metaSrc);
				}
				InputOutput ioFile = getIO(context, metaSrc);
				if (ioFile.getSize() > 0) {
					XmlDoc xmlMeta = parseMetaFile(xmlDoc,context,ioFile,validate);
					xmlDoc.addIncludes(xmlMeta.getIncludes());
					xmlDoc.setExpiresTime(xmlMeta.getExpiresTime());
				}
			}
			catch (Exception e) {
				String errorMsg = "Can not include meta file " + metaSrc + ": " + e.getMessage();
				logger.warning(errorMsg);
				throw new Exception(errorMsg);
			}
		}
	}

	private String findMetaFileSrc(MetaFileType metaFileType, DocumentInfo info) {
		switch (metaFileType) {
		case SELF:
			return info.getSelfMeta();
		case PARENT:
			return  info.getParentMeta();
		case GROUP:
			return  info.getGroupMeta();
		case ROOT:
			return  info.getRootMeta();
		case GLOBAL:
			return  info.getGlobalMeta();
		}
		return null;
	}

	protected XmlDoc parseMetaFile(XmlDoc xmlDoc,RequestContext context, InputOutput ioFile, boolean validate) throws Exception{
		Parser p = getParser(context);
		XmlDoc xmlMeta = p.parse(ioFile, validate);
		Element root = xmlMeta.getDocument().getDocumentElement();
		root = (Element) xmlDoc.getDocument().importNode(root, true);
		xmlDoc.getDocument().getDocumentElement().appendChild(root);
		return xmlMeta;
	}


	/**
	 * Возвращает парсер XML
	 * @return
	 */
	protected Parser getParser(RequestContext context) throws Exception {
		IncludableParser p = new IncludableParser();
		p.setCache(context.getXmlCache());
		return p;
	}

}
