package com.brilliance.mda.runtime.mda.impl;

import com.brilliance.mda.runtime.mda.IBaseObject;
import com.brilliance.mda.runtime.mda.IDatafield;
import com.brilliance.mda.runtime.mda.IModule;
import com.brilliance.mda.runtime.mda.IModuleList;
import com.brilliance.mda.runtime.mda.driver.MdaEnv;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@JsonFilter("fieldFilter")
public class ModuleList<T extends IModule> extends ArrayList<T> implements IModuleList<T> {
    /**
     *
     */
    private static final long serialVersionUID = 7746471631508303843L;

    private Map<String, Object> attrMaps = new HashMap<>();

    private IModule _parent;
    private String _name;
    private String _path;
    private int fullDbSize;
    private Class<T> dataClass;
    private static Field parentField;
    private String moduleType;

    static {
        try {
            parentField = AbstractModule.class.getDeclaredField("_parent");
            parentField.setAccessible(true);
        } catch (Exception e) {
        }
    }

    public ModuleList() {
    }

    public ModuleList(String name, IModule parent, Class<T> dataClass) {
        this._parent = parent;
        this._name = name;
        String prefix = parent.getPath();
        if (prefix == null || prefix.length() == 0 || prefix.equals(separator))
            this._path = separator + this._name;
        else
            this._path = prefix + separator + this._name;
        this.dataClass = dataClass;
        this.moduleType = dataClass.getSimpleName().toLowerCase();
    }

    public ModuleList(String name, IModule parent, int initSize, Class<T> dataClass) {
        this(name, parent, dataClass);
        initData(initSize);
    }

    private void initData(int initSize) {
        try {
            for (int i = 0; i < initSize; i++) {
                T module = dataClass.newInstance();
                this.addElement(module);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void clear() {
        super.clear();
        this.fullDbSize = -1;
    }

    @JsonIgnore
    public IModule getParent() {
        return _parent;
    }

    @JsonIgnore
    public String getName() {
        return _name;
    }

    @JsonIgnore
    @Override
    public String getPath() {
        return this._path.toUpperCase();
    }

    //数据库总体数据量
    @JsonIgnore
    public int getFullDbSize() {
        return this.fullDbSize;
    }

    //获取类型数据
    @JsonIgnore
    public Class<T> getDataClass() {
        return this.dataClass;
    }

    @JsonIgnore
    public int fullSize() {
        return this.size();
    }

    public void removeAll() {
        this.clear();
    }

    /**
     * 废弃的方法，不建议使用
     *
     * @param args IDatafield Argument 两项参数
     */
    @Override
    public T add(Object... args) {

        if (args == null || args.length == 0) return null;
        T bean = null;
        try {
            bean = this.getDataClass().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        for (Object obj : args) {
            if (obj instanceof Argument) {
                Argument arg = (Argument) obj;
                setFieldValue(arg.getFieldName(), arg.getValue(), bean);

            } else if (obj instanceof IDatafield) {
                IDatafield df = (IDatafield) obj;
                setFieldValue(df.getName(), df.getValue(), bean);
            }
        }
        this.add(bean);
        updateParentField(bean);
        return bean;
    }

    private void updateParentField(IModule item) {
        try {
            parentField.set(item, this);
        } catch (Exception e) {
        }
    }

    @Override
    public void addElement(T em) {
        em.assignSerialNum();
        super.add(em);
        updateParentField(em);
    }

    @Override
    public void addElement(int idx, T em) {
        em.assignSerialNum();
        super.add(idx - 1, em);
        updateParentField(em);
    }

    @JsonIgnore
    public T getElement(int idx) {
        return super.get(idx--);
    }

    private void setFieldValue(String name, Object value, T bean) {
        try {
            Field f = this.getDataClass().getDeclaredField(name);
            f.setAccessible(true);
            f.set(bean, value);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

    public void copyValues(IModuleList list) {
        if (list.getDataClass() != this.getDataClass())
            return;
        this.clear();
        this.addAll(list);
    }

    private AbstractScopeEmitter getEmitter() {
        String emitterName = AbstractScopeEmitter.EMITTER_PRE + moduleType;
        AbstractScopeEmitter emitter = (AbstractScopeEmitter) MdaEnv.getBean(emitterName);
        return emitter;
    }

    @Override
    public boolean executeInit() {
        AbstractScopeEmitter emitter = getEmitter();
        if (emitter == null) {
            return true;
        }
        for (int i = 1; i <= this.size(); i++) {
            emitter.acceptContext(this.getElement(i));
            try {
                emitter.executeInit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        emitter.removeContext();
        return true;
    }

    @Override
    public boolean executeInit(int idx) {
        AbstractScopeEmitter emitter = getEmitter();
        if (emitter == null) {
            return true;
        }
        emitter.acceptContext(this.getElement(idx));
        emitter.executeInit();
        emitter.removeContext();
        return true;
    }

    @Override
    public void executeDefault(String... paths) {
        AbstractScopeEmitter emitter = getEmitter();
        if (emitter == null) {
            return;
        }
        for (int i = 1; i <= this.size(); i++) {
            emitter.acceptContext(this.getElement(i));
            if (paths.length == 0) {
                emitter.executeDefaultAll();
            } else {
                for (String path : paths) {
                    emitter.executeDefault(path);
                }
            }
        }
        emitter.removeContext();
    }

    @Override
    public void executeDefault(int idx, String... paths) {
        AbstractScopeEmitter emitter = getEmitter();
        if (emitter == null) {
            return;
        }
        emitter.acceptContext(this.getElement(idx));
        if (paths.length == 0) {
            emitter.executeDefaultAfterInit();
        } else {
            for (String path : paths) {
                emitter.executeDefault(path);
            }
        }
        emitter.removeContext();
    }

    @Override
    public void executeCheckAll() {
        AbstractScopeEmitter emitter = getEmitter();
        if (emitter == null) {
            return;
        }
        String prefix = this._path;
        if (prefix.charAt(0) == '\\') {
            prefix = prefix.substring(1);
        }
        prefix = prefix.replace("\\", ".").toLowerCase();
        for (int i = 1; i <= this.size(); i++) {
            emitter.acceptContext(this.getElement(i));
            MdaEnv.setModuleListCheckItem(prefix + "." + (i - 1));
            emitter.executeCheckAll();
        }
        emitter.removeContext();
    }


    @Override
    public String getFieldPath(String fieldName) {
        return null;
    }

    @Override
    public IBaseObject get(String paramString) {
        return null;
    }

    @Override
    public Map<String, Object> getAttrMaps() {
        return attrMaps;
    }

    public void setAttrMaps(Map<String, Object> attrMaps) {
        this.attrMaps = attrMaps;
    }
}
