package cc.smtweb.framework.core.mvc.controller.binder;

import cc.smtweb.framework.core.cache.AbstractCache;
import cc.smtweb.framework.core.common.SwIpAddr;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.mvc.controller.IEditor;
import cc.smtweb.framework.core.mvc.controller.binder.attr.BeanAttrEditor;
import cc.smtweb.framework.core.mvc.controller.binder.bean.*;
import cc.smtweb.framework.core.mvc.controller.binder.body.BeanBodyEditor;
import cc.smtweb.framework.core.mvc.controller.binder.body.StringBodyEditor;
import cc.smtweb.framework.core.mvc.controller.binder.body.SwMapBodyEditor;
import cc.smtweb.framework.core.mvc.controller.binder.param.*;
import cc.smtweb.framework.core.mvc.controller.binder.path.PathParamEditor;
import cc.smtweb.framework.core.mvc.scheduler.SchedulerPoint;
import cc.smtweb.framework.core.session.UserSession;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;

@Slf4j
public class WebDataBinder {
    private ParamEditor paramEditor = new ParamEditor(new BeanEditor());
    private ParamEditor bodyEditor = new ParamEditor(new BeanBodyEditor());
    private ParamEditor pathEditor = new ParamEditor(new PathParamEditor());
    private ParamEditor headerEditor = new ParamEditor(new NullEditor());
    private ParamEditor attributeEditor = new ParamEditor(new BeanAttrEditor());
    private IEditor cacheEditor;

    public WebDataBinder(IEditor cacheEditor) {
        this.cacheEditor = cacheEditor;
        initParamEditor();
        initBodyEditor();
        initHeaderEditor();
    }

//  public <T> void addParamEditor(Class<T> type, IEditor editor) {
//    paramEditor.add(String.class, editor);
//  }

    public IEditor findEditor(Class<?> paramType, int bindType) {
        IEditor result;

        switch (bindType) {
            case ParamEditor.TYPE_UNKNOWN:
            case ParamEditor.TYPE_PARAM:
            case ParamEditor.TYPE_FIELD:
                if (AbstractCache.class.isAssignableFrom(paramType)) {
                    result = cacheEditor;
                } else {
                    result = paramEditor.find(paramType);
                }
                break;
            case ParamEditor.TYPE_BODY:
                // @SwBody
                result = bodyEditor.find(paramType);
                break;
            case ParamEditor.TYPE_PATH:
                result = pathEditor.find(paramType);
                break;
            case ParamEditor.TYPE_HEADER:
                result = headerEditor.find(paramType);
                break;
            case ParamEditor.TYPE_ATTR:
                result = attributeEditor.find(paramType);
                break;
            default:
                throw new IllegalStateException("Unexpected type value: " + bindType);
        }

        return result;
    }

    private void initParamEditor() {
        TimestampEditor timestampEditor = new TimestampEditor(ParamEditor.TYPE_PARAM);
        paramEditor.add(String.class, new StringEditor(ParamEditor.TYPE_PARAM));
        paramEditor.add(Timestamp.class, timestampEditor);
        paramEditor.add(java.util.Date.class, timestampEditor);
        paramEditor.add(java.sql.Date.class, new DateEditor(ParamEditor.TYPE_PARAM));
        paramEditor.add(java.sql.Time.class, new TimeEditor(ParamEditor.TYPE_PARAM));

        paramEditor.add(Byte.TYPE, new ByteEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Long.TYPE, new LongEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Double.TYPE, new DoubleEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Float.TYPE, new FloatEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Short.TYPE, new ShortEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Integer.TYPE, new IntegerEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Boolean.TYPE, new BooleanEditor(ParamEditor.TYPE_PARAM, true));
        paramEditor.add(Character.TYPE, new CharEditor(ParamEditor.TYPE_PARAM, true));

        paramEditor.add(Byte.class, new ByteEditor(ParamEditor.TYPE_PARAM, false));
        paramEditor.add(Long.class, new LongEditor(ParamEditor.TYPE_PARAM, false));
        paramEditor.add(Double.class, new DoubleEditor(ParamEditor.TYPE_PARAM, false));
        paramEditor.add(Float.class, new FloatEditor(ParamEditor.TYPE_PARAM, false));
        paramEditor.add(Short.class, new ShortEditor(ParamEditor.TYPE_PARAM, false));
        paramEditor.add(Integer.class, new IntegerEditor(ParamEditor.TYPE_PARAM, false));
        paramEditor.add(Boolean.class, new BooleanEditor(ParamEditor.TYPE_PARAM, false));

        paramEditor.add(HttpServletRequest.class, new HttpServletRequestEditor());
//    paramEditor.add(HttpServletResponse.class, new HttpServletResponseEditor());
        paramEditor.add(UserSession.class, new UserSessionEditor());
        paramEditor.add(SwIpAddr.class, new SwIpAddrEditor());
        paramEditor.add(SchedulerPoint.class, new BeanTypeEditor());
    }

    private void initBodyEditor() {
        TimestampEditor timestampEditor = new TimestampEditor(ParamEditor.TYPE_BODY);
        bodyEditor.add(String.class, new StringEditor(ParamEditor.TYPE_BODY));
        bodyEditor.add(Timestamp.class, timestampEditor);
        bodyEditor.add(java.util.Date.class, timestampEditor);
        bodyEditor.add(java.sql.Date.class, new DateEditor(ParamEditor.TYPE_BODY));
        bodyEditor.add(java.sql.Time.class, new TimeEditor(ParamEditor.TYPE_BODY));

        bodyEditor.add(Byte.TYPE, new ByteEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Long.TYPE, new LongEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Double.TYPE, new DoubleEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Float.TYPE, new FloatEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Short.TYPE, new ShortEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Integer.TYPE, new IntegerEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Boolean.TYPE, new BooleanEditor(ParamEditor.TYPE_BODY, true));
        bodyEditor.add(Character.TYPE, new CharEditor(ParamEditor.TYPE_BODY, true));

        bodyEditor.add(Byte.class, new ByteEditor(ParamEditor.TYPE_BODY, false));
        bodyEditor.add(Long.class, new LongEditor(ParamEditor.TYPE_BODY, false));
        bodyEditor.add(Double.class, new DoubleEditor(ParamEditor.TYPE_BODY, false));
        bodyEditor.add(Float.class, new FloatEditor(ParamEditor.TYPE_BODY, false));
        bodyEditor.add(Short.class, new ShortEditor(ParamEditor.TYPE_BODY, false));
        bodyEditor.add(Integer.class, new IntegerEditor(ParamEditor.TYPE_BODY, false));
        bodyEditor.add(Boolean.class, new BooleanEditor(ParamEditor.TYPE_BODY, false));

        bodyEditor.add(String.class, new StringBodyEditor());
        bodyEditor.add(SwMap.class, new SwMapBodyEditor());
    }

    private void initHeaderEditor() {
        TimestampEditor timestampEditor = new TimestampEditor(ParamEditor.TYPE_HEADER);
        headerEditor.add(String.class, new StringEditor(ParamEditor.TYPE_HEADER));
        headerEditor.add(Timestamp.class, timestampEditor);
        headerEditor.add(java.util.Date.class, timestampEditor);
        headerEditor.add(java.sql.Date.class, new DateEditor(ParamEditor.TYPE_HEADER));
        headerEditor.add(java.sql.Time.class, new TimeEditor(ParamEditor.TYPE_HEADER));

        headerEditor.add(Byte.TYPE, new ByteEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Long.TYPE, new LongEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Double.TYPE, new DoubleEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Float.TYPE, new FloatEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Short.TYPE, new ShortEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Integer.TYPE, new IntegerEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Boolean.TYPE, new BooleanEditor(ParamEditor.TYPE_HEADER, true));
        headerEditor.add(Character.TYPE, new CharEditor(ParamEditor.TYPE_HEADER, true));

        headerEditor.add(Byte.class, new ByteEditor(ParamEditor.TYPE_HEADER, false));
        headerEditor.add(Long.class, new LongEditor(ParamEditor.TYPE_HEADER, false));
        headerEditor.add(Double.class, new DoubleEditor(ParamEditor.TYPE_HEADER, false));
        headerEditor.add(Float.class, new FloatEditor(ParamEditor.TYPE_HEADER, false));
        headerEditor.add(Short.class, new ShortEditor(ParamEditor.TYPE_HEADER, false));
        headerEditor.add(Integer.class, new IntegerEditor(ParamEditor.TYPE_HEADER, false));
        headerEditor.add(Boolean.class, new BooleanEditor(ParamEditor.TYPE_HEADER, false));
    }

    public void bindParam(Class<?> type, IEditor editor) {
        paramEditor.add(type, editor);
    }

    public void bindBody(Class<?> type, IEditor editor) {
        bodyEditor.add(type, editor);
    }

    /**
     * 绑定自定义参数读取器
     *
     * @param bindType 绑定类型ParamEditor.TYPE_XXX
     * @param type     参数类型
     * @param editor   参数读取器
     */
    public void bind(int bindType, Class<?> type, IEditor editor) {
        switch (bindType) {
            case ParamEditor.TYPE_PARAM:
                paramEditor.add(type, editor);
                break;
            case ParamEditor.TYPE_BODY:
                // @SwBody
                bodyEditor.add(type, editor);
                break;
            case ParamEditor.TYPE_PATH:
                pathEditor.add(type, editor);
                break;
            case ParamEditor.TYPE_HEADER:
                headerEditor.add(type, editor);
                ;
                break;
            case ParamEditor.TYPE_ATTR:
                attributeEditor.add(type, editor);
                break;
            default:
                throw new IllegalStateException("Unexpected type value: " + bindType);
        }
    }

    /**
     * 绑定默认自定义参数读取器
     *
     * @param bindType 绑定类型ParamEditor.TYPE_XXX
     * @param editor   默认参数读取器
     */
    public void bindDefault(int bindType, IEditor editor) {
        switch (bindType) {
            case ParamEditor.TYPE_PARAM:
                paramEditor.setDefaultEditor(editor);
                break;
            case ParamEditor.TYPE_BODY:
                // @SwBody
                bodyEditor.setDefaultEditor(editor);
                break;
            case ParamEditor.TYPE_PATH:
                pathEditor.setDefaultEditor(editor);
                break;
            case ParamEditor.TYPE_HEADER:
                headerEditor.setDefaultEditor(editor);
                ;
                break;
            case ParamEditor.TYPE_ATTR:
                attributeEditor.setDefaultEditor(editor);
                break;
            default:
                throw new IllegalStateException("Unexpected type value: " + bindType);
        }
    }
}
