package easydo.technology.core;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import easydo.technology.annotations.ZSFContainer;
import easydo.technology.annotations.ZSFContainers;
import easydo.technology.annotations.ZSFLinkField;
import zmo.technology.lite.core.ZeroException;
import zmo.technology.lite.core.ZeroFolders;
import zmo.technology.lite.core.ZeroFolders.ZeroFolderContainer;

public abstract class ZeroSpringFolder<T extends ZeroSpringFolder<?>> {

    public static class ZeroFoldersJacksonDeserializer extends JsonDeserializer<ZeroJacksonFolders> {

        @Override
        public ZeroJacksonFolders deserialize(JsonParser jsonParser, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            try {
                JSONObject jsonObject = JSONObject.parseObject(jsonParser.readValueAsTree().toString());
                ZeroJacksonFolders folders = new ZeroJacksonFolders();
                for (String key : jsonObject.keySet())
                    folders.put(key, jsonObject.getJSONObject(key).toJavaObject(ZeroFolderContainer.class));
                return folders;
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
        }
    }

    public static class ZeroFoldersFastjsonParser implements ObjectDeserializer, ObjectSerializer {

        @Override
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
                throws IOException {
            serializer.write(object);
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
            JSONObject jsonObject = parser.parseObject();
            ZeroJacksonFolders folders = new ZeroJacksonFolders();
            for (String key : jsonObject.keySet())
                folders.put(key, jsonObject.getJSONObject(key).toJavaObject(ZeroFolderContainer.class));
            return (T) folders;
        }

        @Override
        public int getFastMatchToken() {
            return 0;
        }

    }

    @JsonDeserialize(using = ZeroFoldersJacksonDeserializer.class)
    public static class ZeroJacksonFolders extends ZeroFolders {

    }

    @TableField(exist = false)
    private transient Field linkField;

    @TableField(exist = false)
    private transient ZSFContainers containers;

    @TableField(exist = false)
    @JSONField(deserializeUsing = ZeroFoldersFastjsonParser.class, serializeUsing = ZeroFoldersFastjsonParser.class)
    private ZeroJacksonFolders folders;

    public ZeroSpringFolder() {
        super();
        try {
            initFolder();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private final void initFolder() throws ZeroException {
        Class<?> curc = getClass();
        while ((containers = curc.getAnnotation(ZSFContainers.class)) == null &&
                !(curc = curc.getSuperclass()).getName().equals(Object.class.getName())) {
        }
        if (containers == null)
            throw new ZeroException(
                    "folder need at least one container ,use '@ZSFContainers|@ZSFContainer' annotation class");

        curc = getClass();
        List<Field> links = new ArrayList<>();
        do {
            Field[] fields = curc.getDeclaredFields();
            for (Field field : fields)
                if (field.getAnnotation(ZSFLinkField.class) != null)
                    links.add(field);
            if (links.size() > 1)
                throw new ZeroException("folder need only once data id");
            else if (!links.isEmpty())
                break;
        } while (!(curc = curc.getSuperclass()).getName().equals(Object.class.getName()));

        if (links.isEmpty())
            throw new ZeroException("folder need a data id ,use '@ZeroFolderLink' annotation field");

        linkField = links.get(0);

        folders = new ZeroJacksonFolders();
        for (ZSFContainer container : containers.value())
            folders.addContainer(new ZeroFolderContainer(container.maxLimit(), container.directory()));
    }

    public final String dataID() throws IllegalArgumentException, IllegalAccessException {
        linkField.setAccessible(true);
        String dataID = linkField.get(this).toString();
        linkField.setAccessible(false);
        return dataID;
    }

    public final ZeroJacksonFolders getFolders() throws ZeroException {
        return folders;
    }

    public final void setFolders(ZeroJacksonFolders folders) throws ZeroException {
        this.folders = folders;
        for (ZSFContainer container : containers.value())
            folders.resetContainer(container.directory(), container.maxLimit());
    }

}
