package org.inaetics.dronessimulator.discovery.etcd;

import org.apache.log4j.Logger;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;

import java.io.IOException;
import java.util.*;

/**
 * Discovers configurations and publishes these as services.
 */
public class EtcdConfigDiscoverer implements Runnable {
    private static final Logger logger = Logger.getLogger(EtcdConfigDiscoverer.class);

    /** The discoverer used to find configs. */
    private EtcdDiscoverer discoverer;

    /** The configuration admin used to register config services. */
    private ConfigurationAdmin configurationAdmin;

    private HashMap<String, String> configs = new HashMap<>();

    /**
     * Instantiates a new config discoverer.
     * @param discoverer The etcd discoverer to use to find configs.
     */
    public EtcdConfigDiscoverer(EtcdDiscoverer discoverer, ConfigurationAdmin configurationAdmin) {
        this.discoverer = discoverer;
        this.configurationAdmin = configurationAdmin;

        // Initialize some fields
        this.configs = new HashMap<>();
    }

    @Override
    public void run() {
        logger.info("Started etcd config discoverer");

        Collection<String> currentPaths = this.configs.keySet();

        while(!Thread.interrupted()) {
            // Wait for update in directory
            Collection<String> collectedPaths = this.discoverer.getDiscoverableConfigs(true);
            Collection<String> newPaths = new HashSet<>();
            Collection<String> stalePaths = new HashSet<>(currentPaths);

            logger.debug("Found {} configs in etcd", collectedPaths.size());

            // Process paths
            for (String path : collectedPaths) {
                if (this.configs.containsKey(path)) {
                    logger.debug("Config path {} is still there", path);
                    stalePaths.remove(path);
                } else {
                    logger.debug("Config path {} is new", path);
                    newPaths.add(path);
                }
            }

            newPaths.forEach(this::registerInstance);
            stalePaths.forEach(this::unregisterInstance);

            // Update paths
            currentPaths = this.configs.keySet();
        }

        logger.info("Stopping etcd config discoverer");
    }

    /**
     * Registers a new discovered config with properties located on the given path.
     * @param instancePath The instance path for the config.
     */
    private void registerInstance(String instancePath) {
        logger.debug("Registering {} as a discovered config", instancePath);

        // Split path
        String[] pathSegments = EtcdDiscoverer.splitInstancePath(instancePath);
        String type = pathSegments[0];
        String group = pathSegments[1];
        String name = pathSegments[2];

        // Build and register pid
        String pid = String.join(".", pathSegments);
        this.configs.put(instancePath, pid);

        // Get properties
        Map<String, String> properties = this.discoverer.getProperties(type, group, name);
        Dictionary<String, String> configurationProperties = new Hashtable<>(properties);

        // Register properties
        try {
            Configuration configuration = this.configurationAdmin.getConfiguration(pid);
            configuration.update(configurationProperties);
        } catch (IOException e) {
            logger.error("Error when accessing the configuration admin: {}", e.getMessage());
        }

        logger.info("Discovered config {} has become available", pid);
    }

    /**
     * Unregisters an existing discovered config with properties located on the given path.
     * @param instancePath The instance path for the config.
     */
    private void unregisterInstance(String instancePath) {
        logger.debug("Unregistering {} as a discovered config", instancePath);

        String pid = this.configs.get(instancePath);

        // Remove component from configuration admin
        try {
            this.configurationAdmin.getConfiguration(pid).delete();
        } catch (IOException e) {
            logger.error("Error when accessing the configuration admin: {}", e.getMessage());
        }
        logger.info("Discovered config {} has been removed", pid);

        // Clean up in case the instance comes back
        this.configs.remove(instancePath);
    }
}

@@ -636,24 +636,81 @@
 tionAdmin;%0A%0A
+    /** Map of the already discovered configurations. */%0A
     private 
@@ -2785,24 +2785,69 @@
 tancePath);%0A
+%0A        if (pathSegments.length == 3) %7B%0A    
         Stri
@@ -2877,24 +2877,28 @@
 0%5D;%0A        
+ 
+   
 String group
@@ -2921,24 +2921,28 @@
 1%5D;%0A        
+   
+ 
 String name 
@@ -2969,16 +2969,20 @@
         
+    
 // Build
@@ -2999,32 +2999,36 @@
 ter pid%0A        
+  
+  
 String pid = Str
@@ -3052,24 +3052,28 @@
 hSegments);%0A
+    
         this
@@ -3114,16 +3114,20 @@
         
+    
 // Get p
@@ -3144,16 +3144,20 @@
         
+   
+ 
 Map%3CStri
@@ -3227,24 +3227,28 @@
 oup, name);%0A
+    
         Dict
@@ -3330,24 +3330,28 @@
 );%0A%0A        
+ 
+   
 // Register 
@@ -3361,38 +3361,46 @@
 perties%0A        
+    
 try %7B%0A
+    
             Conf
@@ -3484,16 +3484,20 @@
         
+  
+  
 configur
@@ -3535,32 +3535,36 @@
 rties);%0A        
+    
 %7D catch (IOExcep
@@ -3565,32 +3565,36 @@
 OException e) %7B%0A
+    
             logg
@@ -3671,35 +3671,43 @@
 age());%0A        
+  
+  
 %7D%0A%0A
+    
         logger.i
@@ -3757,24 +3757,120 @@
 ble%22, pid);%0A
+        %7D else %7B%0A            logger.warn(%22Invalid config %7B%7D detected%22, instancePath);%0A        %7D%0A
     %7D%0A%0A    /

Check path before registering a config