package me.chyxion.tigon.mybatis;

import org.w3c.dom.Element;
import org.w3c.dom.Document;
import me.chyxion.tigon.model.BaseModel;
import org.apache.ibatis.parsing.XPathParser;
import org.springframework.core.GenericTypeResolver;

/**
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Feb 21, 2017 17:10:28
 */
public abstract class AbstractOnMissingCreateProcessor implements MapperXmlProcessor {

    /**
     * when missing XPath Expression
     * @param mapperClass mapper class
     * @param xPathParser xpath parser
     * @return true if XPath Expression missing
     */
    public boolean accept(Class<BaseMapper<?, ?>> mapperClass, XPathParser xPathParser) {
        return xPathParser.evalNode(missingXPathExpression()) == null;
    }

    /**
     * append node to document
     * @param mapperClass mapper class
     * @param doc xml document
     */
    public void process(Class<BaseMapper<?, ?>> mapperClass, Document doc) {
        doc.getDocumentElement().appendChild(elementToAppend(mapperClass, doc));
    }

    /**
     * @return missing XPath expression
     */
    protected abstract String missingXPathExpression();

    /**
     * return element to append to document
     * @param mapperClass mapper class
     * @param doc mapper xml document
     * @return element to append
     */
    protected abstract Element elementToAppend(Class<BaseMapper<?, ?>> mapperClass, Document doc);

    /**
     * get model class from mapper class
     * @param mapperClass mapper class
     * @return model class
     */
    @SuppressWarnings("unchecked")
	protected Class<BaseModel<?>> getModelClass(Class<BaseMapper<?, ?>> mapperClass) {
        return (Class<BaseModel<?>>) GenericTypeResolver.resolveTypeArguments(
        	mapperClass, BaseMapper.class)[1];
    }

    /**
     * new model object
     * @param modelClass model class
     * @return model object
     */
    protected BaseModel<?> newModelObj(Class<BaseModel<?>> modelClass) {
        try {
            return modelClass.newInstance();
        }
        catch (Exception e) {
            throw new IllegalStateException(
                "Create Model [" + modelClass + "] Instance Error Caused", e);
        }
    }

    /**
     * append include element to node
     * @param doc doc
     * @param nodeEl node element
     * @param refid include refid
     * @return element
     */
    protected Element appendIncludeEl(Document doc, Element nodeEl, String refid) {
        Element includeEl = doc.createElement("include");
        includeEl.setAttribute("refid", refid);
        nodeEl.appendChild(includeEl);
        return nodeEl;
    }

    /**
     * create non-count select element
     * @param mapperClass mapper class
     * @param doc doc
     * @param id node id
     * @return element
     */
    protected Element createSelectEl(Class<BaseMapper<?, ?>> mapperClass, Document doc, String id) {
        return createSelectEl(doc, id, getModelClass(mapperClass).getName());
    }

    /**
     * create select element
     * @param doc doc
     * @param id node element id [find, list, count]
     * @param resultType select result type [map, int, className]
     * @return select element
     */
    protected Element createSelectEl(Document doc, String id, String resultType) {
        Element selectEl = doc.createElement("select");
        selectEl.setAttribute("id", id);
        selectEl.setAttribute("resultType", resultType);
        return selectEl;
    }
}
