/*
 * Copyright © 2016 by Data Virtuality GmbH
 */
package com.datavirtuality.dv.core.interfaces;

import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrMatcher;
import org.apache.commons.lang3.text.StrSubstitutor;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Uniform properties holder.
 *
 * @author Roman Pedchenko
 * @date 2016.08.27
 *
 * @author Dmitry Pogorelov
 * @date 2024.06.25
 */
public class DVPropertyHolder {
    public enum Type {
        QUARTZ("quartz"),
        LDAP("ldap");

        private String propertyName;

        Type(String propertyName) {
            this.propertyName = propertyName;
        }

        public String getPropertyName() {
            return propertyName;
        }
    }

    public static class SingletonHolder {
        public static final DVPropertyHolder HOLDER_INSTANCE = new DVPropertyHolder();
    }

    public static DVPropertyHolder getInstance() {
        return SingletonHolder.HOLDER_INSTANCE;
    }

    private DVPropertyHolder() {
    }

    private final Map<String, Properties> map = new HashMap<>();

    public void add(String name, Properties props) {
        final StrSubstitutor sub = new StrSubstitutor(StrLookup.systemPropertiesLookup(), StrSubstitutor.DEFAULT_PREFIX,
                StrSubstitutor.DEFAULT_SUFFIX, StrSubstitutor.DEFAULT_ESCAPE, StrMatcher.stringMatcher(":"));
        props.forEach((k, v) -> props.put(k, sub.replace(v)));
        map.put(name.toUpperCase(), props);
    }

    public void add(Type type, Properties props) {
        add(type.getPropertyName(), props);
    }

    public Properties get(String name) {
        Properties result = map.get(name.toUpperCase());
        if (result == null)
            result = new Properties();
        return result;
    }

    private <T> T getProperty(Type type, String name) {
        final Properties props = get(type.getPropertyName());
        return (T) props.get(name);
    }

    public int getInt(Type type, String name, int defVal) {
        return new PropertyGetter(this) {
            @Override
            Integer getValue(String propThrs) {
                return Integer.parseInt(propThrs);
            }
        }.getValue(type, name, defVal);
    }

    public boolean getBoolean(Type type, String name, boolean defVal) {
        return new PropertyGetter(this) {
            @Override
            Boolean getValue(String propThrs) {
                return Boolean.valueOf(propThrs);
            }
        }.getValue(type, name, defVal);
    }

    public String getString(Type type, String name, String defVal) {
        return new PropertyGetter(this) {
            @Override
            String getValue(String propThrs) {
                return propThrs;
            }
        }.getValue(type, name, defVal);
    }

    private abstract static class PropertyGetter {
        private DVPropertyHolder dvPropertyHolder;

        public PropertyGetter(DVPropertyHolder dvPropertyHolder) {
            this.dvPropertyHolder = dvPropertyHolder;
        }

        abstract <T> T getValue(String propThrs) throws Exception;

        public <T> T getValue(Type type, String name, T defVal) {
            String propThrs = dvPropertyHolder.getProperty(type, name);
            T value = defVal;
            if (propThrs != null)
                try {
                    value = getValue(propThrs);
                } catch (Exception e) {
                    return defVal;
                }
            return value;
        }
    }
}
