/*
 * (C) Copyright 2016 Kurento (http://kurento.org/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.kurento.commons;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.kurento.commons.PropertiesManager.PropertyHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;

public class ConfigFilePropertyHolder implements PropertyHolder {

  private static Logger log = LoggerFactory.getLogger(ConfigFilePropertyHolder.class);

  private static final String SINGLE_CONFIG_FILES_PROPERTY = "single.config.file";

  private static Path lastLoadedConfigFilePath;

  private List<ConfigFileObject> loadedConfigFiles;

  private Set<Path> loadedPaths;

  private static final Gson gson = new GsonBuilder().create();

  public static synchronized void configurePropertiesFromConfigFile(Path configFilePath)
      throws JsonSyntaxException, JsonIOException, IOException {

    boolean singleConfigFile = Boolean.getBoolean(SINGLE_CONFIG_FILES_PROPERTY);
    if (singleConfigFile && lastLoadedConfigFilePath != null) {
      log.warn("Trying to load a second config file. The first was {} and the "
          + "current is {}. Ignoring it.", lastLoadedConfigFilePath, configFilePath);
      return;
    }

    ConfigFilePropertyHolder cfph = null;
    PropertyHolder ph = PropertiesManager.getPropertyHolder();
    if (ph != null && ph instanceof ConfigFilePropertyHolder) {
      cfph = (ConfigFilePropertyHolder) ph;
      if (cfph.loadedPaths != null) {
        if (cfph.loadedPaths.contains(configFilePath)) {
          log.debug("Trying to load again config file {}. Ignoring it.",
              configFilePath.toAbsolutePath());
          return;
        }
      }
    }

    lastLoadedConfigFilePath = configFilePath;

    // FIXME shouldn't this be the first sentence in the method?
    if (configFilePath == null) {
        throw new AssertionError("configFilePath paramter must be not null.");
    }

    log.debug("Using configuration file in path '{}' ({})", configFilePath,
        configFilePath.getClass().getCanonicalName());

    JsonReader reader =
        new JsonReader(Files.newBufferedReader(configFilePath, StandardCharsets.UTF_8));

    reader.setLenient(true);

    JsonObject configFile = gson.fromJson(reader, JsonObject.class);

    traceConfigContent(configFile);

    if (cfph == null) {
      cfph = new ConfigFilePropertyHolder();
    }
    cfph.loadedConfigFiles.add(new ConfigFileObject(configFilePath, configFile));
    cfph.loadedPaths.add(configFilePath);

    PropertiesManager.setPropertyHolder(cfph);
  }

  private static void traceConfigContent(JsonObject configFile) {
    if (log.isDebugEnabled()) {
      Gson gs = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
      String jsonContents = gs.toJson(configFile);
      log.debug("Configuration content: " + jsonContents);
    }
  }

  public ConfigFilePropertyHolder() {
    this.loadedConfigFiles = new ArrayList<ConfigFileObject>();
    this.loadedPaths = new HashSet<Path>();
  }

  @Override
  public String getProperty(String property) {
    String systemProperty = System.getProperty(property);

    if (systemProperty != null) {
      return systemProperty;
    }

    String[] tokens = property.split("\\.");

    int lastTokenNumber = tokens.length - 1;

    // TODO customize search order (by default, it starts from the first loaded file)
    // e.g. system property 'reverseSearch.multi.config.file': if true, start searching in reverse
    if (loadedConfigFiles != null) {

      for (ConfigFileObject configFileObj : loadedConfigFiles) {
        Path currentPath = configFileObj.getConfigFilePath();
        JsonObject currentObject = configFileObj.getConfigFile();

        for (int i = 0; i < tokens.length; i++) {
          JsonElement element = currentObject.get(tokens[i]);
          if (element == null) {
            break; // goto next cfg file
          }

          if (i == lastTokenNumber) {
            log.debug("Found {} in config file {}", property, currentPath.toAbsolutePath());
            if (element instanceof JsonPrimitive) {
              return element.getAsString();
            } else {
              return element.toString();
            }
          }

          try {
            currentObject = (JsonObject) element;
          } catch (ClassCastException e) {
            break; // goto next cfg file
          }
        }
      }

    }

    return null;
  }

  public List<ConfigFileObject> getLoadedConfigFiles() {
    return loadedConfigFiles;
  }
}

@@ -986,16 +986,61 @@
 ctory;%0A%0A
+import com.google.common.base.Preconditions;%0A
 import c
@@ -2851,71 +2851,51 @@
     
-if (configFilePath == null) %7B%0A        throw new AssertionError(
+Preconditions.checkNotNull(configFilePath, 
 %22con
@@ -2935,22 +2935,16 @@
 null.%22);
-%0A    %7D
 %0A%0A    lo

Revert "Remove use of Preconditions in ConfigFilePropertyHolder"