package net.northking.cctp.element.core;

import net.northking.cctp.element.util.Assert;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * 组件库实例运行时预设的配置信息
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public interface PresetConfiguration {

    /**
     * Get default preset configuration parameters
     *
     * @return
     */
    default PresetParamList getDefaultPresetParams() {
        return new PresetParamList();
    }

    /**
     * Preset parameter list
     */
    class PresetParamList {
        private Map<String, PresetParam> map = new HashMap<>();

        public PresetParamList add(PresetParam param) {
            Assert.notNull(param);
            map.put(param.name, param);
            return this;
        }

        public Map<String, PresetParam> asMap() {
            return Collections.unmodifiableMap(map);
        }

        public List<PresetParam> getAll() {
            List<PresetParam> result = new ArrayList<>();
            result.addAll(map.values());
            return result;
        }
    }

    /**
     * Preset parameter type
     */
    enum PresetParamType {
        ENUM, INTEGER, STRING, BOOLEAN
    }

    enum PresetParamScope {
        SYSTEM, USER
    }

    /**
     * Preset parameter definition
     */
    abstract class PresetParam {

        protected String name;
        protected String defaultValue;
        protected PresetParamType type;
        protected String[] enumValues;
        protected PresetParamScope scope = PresetParamScope.SYSTEM;

        public PresetParam(String name, String defaultValue, PresetParamType type) {
            Assert.notNull(name);
            Assert.notNull(defaultValue);
            Assert.notNull(type);
            this.name = name;
            this.defaultValue = defaultValue;
            this.type = type;
        }

        public PresetParam(String name, String defaultValue, PresetParamType type, PresetParamScope scope) {
            this(name, defaultValue, type);
            this.scope = scope;
        }

        public PresetParam(String name, String defaultValue, PresetParamType type, String[] enumValues) {
            this(name, defaultValue, type);
            this.enumValues = enumValues;
        }

        public PresetParam(String name, String defaultValue, PresetParamType type, String[] enumValues,
                           PresetParamScope scope) {
            this(name, defaultValue, type, enumValues);
            this.scope = scope;
        }

        public String getName() {
            return name;
        }

        public String getDefaultValue() {
            return defaultValue;
        }

        public PresetParamType getType() {
            return type;
        }

        public String[] getEnumValues() {
            return enumValues;
        }

        public abstract boolean validate(String value);
    }

    class StringParam extends PresetParam {

        public StringParam(String name, String defaultValue) {
            super(name, defaultValue, PresetParamType.STRING);
        }

        public StringParam(String name, String defaultValue, PresetParamScope scope) {
            super(name, defaultValue, PresetParamType.STRING, scope);
        }

        @Override
        public boolean validate(String value) {
            return true;
        }
    }

    class EnumParam extends PresetParam {

        public EnumParam(String name, String defaultValue, String[] enumValues) {
            super(name, defaultValue, PresetParamType.ENUM, enumValues);
        }

        public EnumParam(String name, String defaultValue, String[] enumValues, PresetParamScope scope) {
            super(name, defaultValue, PresetParamType.ENUM, enumValues, scope);
        }

        @Override
        public boolean validate(String value) {
            return StringUtils.equalsAny(value, enumValues);
        }
    }

    class BooleanParam extends PresetParam {

        public BooleanParam(String name, boolean defaultValue) {
            super(name, String.valueOf(defaultValue), PresetParamType.BOOLEAN);
        }

        public BooleanParam(String name, String defaultValue, PresetParamScope scope) {
            super(name, defaultValue, PresetParamType.BOOLEAN, scope);
        }

        @Override
        public String getDefaultValue() {
            return StringUtils.lowerCase(defaultValue);
        }

        @Override
        public boolean validate(String value) {
            return StringUtils.equalsAnyIgnoreCase(value, "true", "false");
        }
    }

    class IntegerParam extends PresetParam {

        public IntegerParam(String name, int defaultValue) {
            super(name, String.valueOf(defaultValue), PresetParamType.INTEGER);
        }

        public IntegerParam(String name, String defaultValue, PresetParamScope scope) {
            super(name, defaultValue, PresetParamType.INTEGER, scope);
        }

        @Override
        public boolean validate(String value) {
            try {
                Integer.parseInt(value);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }
}