package org.ovirt.engine.core.utils.osinfo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.prefs.Preferences;

import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.common.osinfo.MapBackedPreferences;
import org.ovirt.engine.core.common.osinfo.OsRepositoryImpl;
import org.ovirt.engine.core.utils.EngineLocalConfig;
import org.ovirt.engine.core.utils.OsLoader;
import org.ovirt.engine.core.utils.log.Log;
import org.ovirt.engine.core.utils.log.LogFactory;

public enum OsInfoPreferencesLoader implements OsLoader {

    INSTANCE;

    /**
     * The source properties from which all values are loaded.
     */
    private Properties properties;
    /**
     * The configuration tree. See {@link java.util.prefs.Preferences}
     */
    private MapBackedPreferences preferences = new MapBackedPreferences(null, "");

    private Log log = LogFactory.getLog(OsInfoPreferencesLoader.class);

    public void init(Path directoryPath) {
        load(directoryPath);
        loadPreferencesFromProperties();
    }

    void load(Path directoryPath) {
        File dir = directoryPath.toFile();
        if (dir.exists()) {
            File[] files = directoryPath.toFile().listFiles(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    return name.endsWith(".properties");
                }
            });

            // load files by name order. 00-osinfo.properties should be loaded first
            if (files != null) {
                Arrays.sort(files);

                for (File file : files) {
                    loadFile(file.toPath());
                }
            }
        } else {
            log.errorFormat("directory {0} doesn't exist.", dir.getPath());
        }

    }

    @Override
    public MapBackedPreferences getPreferences() {
        return preferences;
    }

    private void loadFile(Path path) {
        try(BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            properties = new Properties(properties);
            properties.load(reader);

            log.info("Loaded file " + path);
        } catch (IOException e) {
            log.error("Failed loading file "  + path);
        }

    }

    /**
     * convert the property key namepaces (key.name.space) to the hierarchical preferences node<br>
     * is done by breaking each dot separated part and creating a node out of it.<br>
     * The last node is being set with the value. Versioned value path are denoted as "key.name.space.value.x.y" and non<br>
     * versioned value are just "key.name.space.value" or just "key.name.space"<br>
     */
    private void loadPreferencesFromProperties() {
        if (properties != null) {
            for (String propertyKey : properties.stringPropertyNames()) {
                Preferences node = preferences;
                Iterator<String> iterator = Arrays.asList(propertyKey.split("\\.|\\.value")).iterator();
                String key = null;
                while (iterator.hasNext()) {
                    key = iterator.next();
                    // create a node from each dot separated part of the key
                    // if its "value" joining the reset of the iterator so "value" or "value.3.1" are
                    // considered as the leaf and should be set with the designated value
                    if (key.startsWith("value")) {
                        key = iterator.hasNext() ? key + "." + StringUtils.join(iterator, ".") : key;
                        break;
                    } else {
                        node = node.node(key);
                    }
                }
                putValue(node, key, properties.getProperty(propertyKey));
            }
        }
        // properties isn't needed anymore
        properties = null;
    }

    private void putValue(Preferences node, String key, String value) {
        node.put(key, value);
    }

    public static void main(String[] args) {
        INSTANCE.init(FileSystems.getDefault().getPath(EngineLocalConfig.getInstance().getEtcDir().getAbsolutePath(),
                "/conf/osinfo.conf.d"));
        OsRepositoryImpl.INSTANCE.init(INSTANCE.getPreferences());
        for (Map.Entry<Integer, String> e : OsRepositoryImpl.INSTANCE.getOsNames().entrySet()) {
            System.out.println(e);
        }
        System.out.println(OsRepositoryImpl.INSTANCE.getMaximumRam(100, null));
    }
}

@@ -195,42 +195,8 @@
 ts;%0A
-import java.nio.file.FileSystems;%0A
 impo
@@ -215,24 +215,24 @@
 file.Files;%0A
+
 import java.
@@ -302,30 +302,8 @@
 or;%0A
-import java.util.Map;%0A
 impo
@@ -408,16 +408,16 @@
 gUtils;%0A
+
 import o
@@ -477,123 +477,8 @@
 es;%0A
-import org.ovirt.engine.core.common.osinfo.OsRepositoryImpl;%0Aimport org.ovirt.engine.core.utils.EngineLocalConfig;%0A
 impo
@@ -4100,505 +4100,6 @@
  %7D%0A%0A
-    public static void main(String%5B%5D args) %7B%0A        INSTANCE.init(FileSystems.getDefault().getPath(EngineLocalConfig.getInstance().getEtcDir().getAbsolutePath(),%0A                %22/conf/osinfo.conf.d%22));%0A        OsRepositoryImpl.INSTANCE.init(INSTANCE.getPreferences());%0A        for (Map.Entry%3CInteger, String%3E e : OsRepositoryImpl.INSTANCE.getOsNames().entrySet()) %7B%0A            System.out.println(e);%0A        %7D%0A        System.out.println(OsRepositoryImpl.INSTANCE.getMaximumRam(100, null));%0A    %7D%0A
 %7D%0A

clean unused "main" blocks