package org.cobbzilla.util.yml;

import com.github.mustachejava.DefaultMustacheFactory;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * (c) Copyright 2013 Jonathan Cobb
 * This code is available under the Apache License, version 2: http://www.apache.org/licenses/LICENSE-2.0.html
 */
public class YmlMerger {

    private static final Logger LOG = LoggerFactory.getLogger(YmlMerger.class);
    public static final DefaultMustacheFactory DEFAULT_MUSTACHE_FACTORY = new DefaultMustacheFactory();

    private final Yaml yaml = new Yaml();
    private final Map<String, Object> scope = new HashMap<String, Object>();;

    public YmlMerger() {
        init(System.getenv());
    }

    public YmlMerger(Map<String, String> env) {
        init(env);
    }

    private void init(Map<String, String> env) {
        for (String varname : env.keySet()) {
            scope.put(varname, env.get(varname));
        }
    }

    public Map<String, Object> merge(String[] files) throws IOException {
        Map<String, Object> mergedResult = new LinkedHashMap<String, Object>();
        for (String file : files) {
            InputStream in = null;
            try {
                in = new FileInputStream(file);
                String entireFile = IOUtils.toString(in);
                final StringWriter writer = new StringWriter(entireFile.length() + 10);
                DEFAULT_MUSTACHE_FACTORY.compile(new StringReader(entireFile), "mergeyml_"+System.currentTimeMillis()).execute(writer, scope);
                final Map<String, Object> yamlContents = (Map<String, Object>) yaml.load(writer.toString());
                merge_internal(mergedResult, yamlContents);
                LOG.info("loaded YML from "+file+": "+yamlContents);

            } finally {
                if (in != null) in.close();
            }
        }
        return mergedResult;
    }

    private void merge_internal(Map<String, Object> mergedResult, Map<String, Object> yamlContents) {

        for (String key : yamlContents.keySet()) {

            Object yamlValue = yamlContents.get(key);
            if (yamlValue == null) {
                addToMergedResult(mergedResult, key, yamlValue);
                continue;
            }

            Object existingValue = mergedResult.get(key);
            if (existingValue != null) {
                if (yamlValue instanceof Map) {
                    if (existingValue instanceof Map) {
                        merge_internal((Map<String, Object>) existingValue, (Map<String, Object>)  yamlValue);
                    } else if (existingValue instanceof String) {
                        throw new IllegalArgumentException("Cannot merge complex element into a simple element: "+key);
                    } else {
                        throw unknownValueType(key, yamlValue);
                    }

                } else if (yamlValue instanceof String ||
                        yamlValue instanceof Boolean ||
                        yamlValue instanceof Double ||
                        yamlValue instanceof Integer) {
                    LOG.info("overriding value of "+key+" with value "+yamlValue);
                    addToMergedResult(mergedResult, key, yamlValue);

                } else {
                    throw unknownValueType(key, yamlValue);
                }

            } else {
                if (yamlValue instanceof Map ||
                        yamlValue instanceof String ||
                        yamlValue instanceof Boolean||
                        yamlValue instanceof Integer||
                        yamlValue instanceof Double) {
                    LOG.info("adding new key->value: "+key+"->"+yamlValue);
                    addToMergedResult(mergedResult, key, yamlValue);
                } else {
                    throw unknownValueType(key, yamlValue);
                }
            }
        }
    }

    private IllegalArgumentException unknownValueType(String key, Object yamlValue) {
        final String msg = "Cannot merge element of unknown type: " + key + ": " + yamlValue.getClass().getName();
        LOG.error(msg);
        return new IllegalArgumentException(msg);
    }

    private Object addToMergedResult(Map<String, Object> mergedResult, String key, Object yamlValue) {
        return mergedResult.put(key, yamlValue);
    }

    public String mergeToString(String[] files) throws IOException {
        return toString(merge(files));
    }

    public String toString(Map<String, Object> merged) {
        return yaml.dump(merged);
    }

}

@@ -1333,24 +1333,71 @@
       try %7B%0A
+                // read the file into a String%0A
             
@@ -1448,16 +1448,22 @@
         
+final 
 String e
@@ -1495,16 +1495,57 @@
 ing(in);
+%0A%0A                // substitute variables
 %0A       
@@ -1767,16 +1767,54 @@
  scope);
+%0A%0A                // load the YML file
 %0A       
@@ -1910,24 +1910,67 @@
 toString());
+%0A%0A                // merge into results map
 %0A           

add some comments