text: /**
   * Copyright © 2020 IBM Corporation
   *
   * 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.
   */
  #pragma once
  
  #include "sdbusplus.hpp"
  
  #include <nlohmann/json.hpp>
  #include <phosphor-logging/log.hpp>
  #include <sdbusplus/bus.hpp>
  #include <sdeventplus/source/signal.hpp>
  
  #include <filesystem>
  #include <fstream>
  
  namespace phosphor::fan
  {
  
  namespace fs = std::filesystem;
  using json = nlohmann::json;
  using namespace phosphor::logging;
  
  constexpr auto confOverridePath = "/etc/phosphor-fan-presence";
  constexpr auto confBasePath = "/usr/share/phosphor-fan-presence";
  constexpr auto confDbusPath = "/xyz/openbmc_project/inventory/system/chassis";
  constexpr auto confDbusIntf =
      "xyz.openbmc_project.Inventory.Decorator.Compatible";
  constexpr auto confDbusProp = "Names";
  
  class JsonConfig
  {
    public:
      /**
       * Get the json configuration file. The first location found to contain
       * the json config file for the given fan application is used from the
       * following locations in order.
       * 1.) From the confOverridePath location
       * 2.) From config file found using a property value as a relative
       * path extension on the base path from the dbus object where:
       *     path = Path set in confDbusPath
       *     interface = Interface set in confDbusIntf
       *     property = Property set in confDbusProp
       * 3.) *DEFAULT* - From the confBasePath location
       *
       * @brief Get the configuration file to be used
       *
       * @param[in] bus - The dbus bus object
       * @param[in] appName - The phosphor-fan-presence application name
       * @param[in] fileName - Application's configuration file's name
       *
       * @return filesystem path
       *     The filesystem path to the configuration file to use
       */
      static const fs::path getConfFile(sdbusplus::bus::bus& bus,
                                        const std::string& appName,
                                        const std::string& fileName)
      {
          // Check override location
          fs::path confFile = fs::path{confOverridePath} / appName / fileName;
          if (fs::exists(confFile))
          {
              return confFile;
          }
  
          try
          {
              // Retrieve json config relative path location from dbus
              auto confDbusValue =
                  util::SDBusPlus::getProperty<std::vector<std::string>>(
                      bus, confDbusPath, confDbusIntf, confDbusProp);
              // Look for a config file at each entry relative to the base
              // path and use the first one found
              auto it = std::find_if(
                  confDbusValue.begin(), confDbusValue.end(),
                  [&confFile, &appName, &fileName](auto const& entry) {
                      confFile =
                          fs::path{confBasePath} / appName / entry / fileName;
                      return fs::exists(confFile);
                  });
              if (it == confDbusValue.end())
              {
                  // Property exists, but no config file found. Use default base
                  // path
                  confFile = fs::path{confBasePath} / appName / fileName;
              }
          }
          catch (const util::DBusError&)
          {
              // Property unavailable, attempt default base path
              confFile = fs::path{confBasePath} / appName / fileName;
          }
  
          if (!fs::exists(confFile))
          {
              log<level::ERR>("No JSON config file found",
                              entry("DEFAULT_FILE=%s", confFile.c_str()));
              throw std::runtime_error("No JSON config file found");
          }
  
          return confFile;
      }
  
      /**
       * @brief Load the JSON config file
       *
       * @param[in] confFile - File system path of the configuration file to load
       *
       * @return Parsed JSON object
       *     The parsed JSON configuration file object
       */
      static const json load(const fs::path& confFile)
      {
          std::ifstream file;
          json jsonConf;
  
          if (fs::exists(confFile))
          {
              log<level::INFO>("Loading configuration",
                               entry("JSON_FILE=%s", confFile.c_str()));
              file.open(confFile);
              try
              {
                  jsonConf = json::parse(file);
              }
              catch (std::exception& e)
              {
                  log<level::ERR>("Failed to parse JSON config file",
                                  entry("JSON_FILE=%s", confFile.c_str()),
                                  entry("JSON_ERROR=%s", e.what()));
                  throw std::runtime_error("Failed to parse JSON config file");
              }
          }
          else
          {
              log<level::ERR>("Unable to open JSON config file",
                              entry("JSON_FILE=%s", confFile.c_str()));
              throw std::runtime_error("Unable to open JSON config file");
          }
  
          return jsonConf;
      }
  };
  
  } // namespace phosphor::fan
  