package com.publiccms.common.handler;

import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import com.publiccms.common.base.BaseHandler;
import com.publiccms.common.servlet.SafeHttpRequestHashModel;
import com.publiccms.common.tools.CommonUtils;
import com.publiccms.common.tools.TemplateModelUtils;

import freemarker.core.Environment;
import freemarker.core.Environment.Namespace;
import freemarker.ext.servlet.HttpRequestHashModel;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;

/**
 *
 * TemplateDirectiveHandler
 *
 */
public class TemplateDirectiveHandler extends BaseHandler {
    private Map<String, TemplateModel> parameters;
    private TemplateModel[] loopVars;
    private TemplateDirectiveBody templateDirectiveBody;
    private Environment environment;

    /**
     * @param parameters
     * @param loopVars
     * @param environment
     * @param templateDirectiveBody
     */
    public TemplateDirectiveHandler(Map<String, TemplateModel> parameters, TemplateModel[] loopVars, Environment environment,
            TemplateDirectiveBody templateDirectiveBody) {
        this.parameters = parameters;
        this.loopVars = loopVars;
        this.templateDirectiveBody = templateDirectiveBody;
        this.environment = environment;
        regristerParameters();
    }

    @Override
    public void render() throws TemplateException, IOException {
        Map<String, TemplateModel> reduceMap = reduce();
        if (null != templateDirectiveBody) {
            templateDirectiveBody.render(environment.getOut());
        }
        reduce(reduceMap);
    }

    @Override
    public void print(String value) throws IOException {
        environment.getOut().write(value);
    }

    @Override
    public Writer getWriter() {
        return environment.getOut();
    }

    private Map<String, TemplateModel> reduce() throws TemplateModelException {
        Map<String, TemplateModel> reduceMap = new LinkedHashMap<>();
        ObjectWrapper objectWrapper = environment.getObjectWrapper();
        Namespace namespace = environment.getCurrentNamespace();
        Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
        for (int i = 0; iterator.hasNext(); i++) {
            Entry<String, Object> entry = iterator.next();
            if (i < loopVars.length) {
                loopVars[i] = objectWrapper.wrap(entry.getValue());
            } else {
                String key = entry.getKey();
                reduceMap.put(key, namespace.get(key));
                namespace.put(key, objectWrapper.wrap(entry.getValue()));
            }
        }
        return reduceMap;
    }

    private void reduce(Map<String, TemplateModel> reduceMap) {
        if (CommonUtils.notEmpty(reduceMap)) {
            Namespace namespace = environment.getCurrentNamespace();
            namespace.putAll(reduceMap);
        }
    }

    @Override
    public Map<String, String> getMap(String name) throws TemplateModelException {
        return TemplateModelUtils.converMap(parameters.get(name));
    }

    @Override
    protected String getStringWithoutRegister(String name) throws TemplateModelException {
        return TemplateModelUtils.converString(parameters.get(name));
    }

    @Override
    public Integer getIntegerWithoutRegister(String name) throws TemplateModelException {
        return TemplateModelUtils.converInteger(parameters.get(name));
    }

    @Override
    public Byte getByteWithoutRegister(String name) throws TemplateModelException {
        return TemplateModelUtils.converByte(parameters.get(name));
    }

    @Override
    public Short getShort(String name) throws TemplateModelException {
        regristerParameter(PARAMETER_TYPE_SHORT, name);
        return TemplateModelUtils.converShort(parameters.get(name));
    }

    @Override
    public Long getLong(String name) throws TemplateModelException {
        regristerParameter(PARAMETER_TYPE_LONG, name);
        return TemplateModelUtils.converLong(parameters.get(name));
    }

    @Override
    public Double getDouble(String name) throws TemplateModelException {
        regristerParameter(PARAMETER_TYPE_DOUBLE, name);
        return TemplateModelUtils.converDouble(parameters.get(name));
    }

    @Override
    public BigDecimal getBigDecimal(String name) throws TemplateModelException {
        regristerParameter(PARAMETER_TYPE_DOUBLE, name);
        return TemplateModelUtils.converBigDecimal(parameters.get(name));
    }

    @Override
    protected String[] getStringArrayWithoutRegister(String name) throws TemplateModelException {
        return TemplateModelUtils.converStringArray(parameters.get(name));
    }

    @Override
    protected Boolean getBooleanWithoutRegister(String name) throws TemplateModelException {
        return TemplateModelUtils.converBoolean(parameters.get(name));
    }

    @Override
    public Date getDateWithoutRegister(String name) throws TemplateModelException {
        return TemplateModelUtils.converDate(parameters.get(name));
    }

    @Override
    public Locale getLocale() {
        return environment.getLocale();
    }

    @Override
    public HttpServletRequest getRequest() throws TemplateModelException {
        Object httpRequestHashModel = environment.getGlobalVariable("Request");
        if (null != httpRequestHashModel) {
            if (httpRequestHashModel instanceof HttpRequestHashModel) {
                return ((HttpRequestHashModel) httpRequestHashModel).getRequest();
            } else if (httpRequestHashModel instanceof SafeHttpRequestHashModel) {
                return ((SafeHttpRequestHashModel) httpRequestHashModel).getRequest();
            }
        }
        return null;
    }

    @Override
    public Object getAttribute(String name) throws TemplateModelException {
        TemplateModel model = environment.getVariable(name);
        if (null != model) {
            return TemplateModelUtils.converBean(model);
        }
        return null;
    }
    
    @Override
    public String getStringAttribute(String name) throws TemplateModelException {
        TemplateModel model = environment.getVariable(name);
        if (null != model) {
            return TemplateModelUtils.converString(model);
        }
        return null;
    }

    @Override
    public boolean inHttp() {
        return false;
    }
}