text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2015 The Bitcoin Core developers
  // Copyright (c) 2014-2019 The Dash Core developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  /**
   * Server/client environment: argument handling, config file parsing,
   * logging, thread wrappers, startup time
   */
  #ifndef BITCOIN_UTIL_H
  #define BITCOIN_UTIL_H
  
  #if defined(HAVE_CONFIG_H)
  #include <config/jokecoin-config.h>
  #endif
  
  #include <compat.h>
  #include <fs.h>
  #include <sync.h>
  #include <tinyformat.h>
  #include <utiltime.h>
  #include <amount.h>
  
  #include <atomic>
  #include <exception>
  #include <map>
  #include <memory>
  #include <set>
  #include <stdint.h>
  #include <string>
  #include <unordered_map>
  #include <unordered_set>
  #include <vector>
  
  #include <boost/signals2/signal.hpp>
  #include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted
  
  // Debugging macros
  
  // Uncomment the following line to enable debugging messages
  // or enable on a per file basis prior to inclusion of util.h
  //#define ENABLE_JOKECOIN_DEBUG
  #ifdef ENABLE_JOKECOIN_DEBUG
  #define DBG( x ) x
  #else
  #define DBG( x )
  #endif
  
  //Jokecoin only features
  
  extern bool fMasternodeMode;
  extern bool fDisableGovernance;
  extern int nWalletBackups;
  
  // Application startup time (used for uptime calculation)
  int64_t GetStartupTime();
  
  static const bool DEFAULT_LOGTIMEMICROS  = false;
  static const bool DEFAULT_LOGIPS         = false;
  static const bool DEFAULT_LOGTIMESTAMPS  = true;
  static const bool DEFAULT_LOGTHREADNAMES = false;
  extern const char * const DEFAULT_DEBUGLOGFILE;
  
  /** Signals for translation. */
  class CTranslationInterface
  {
  public:
      /** Translate a message to the native language of the user. */
      boost::signals2::signal<std::string (const char* psz)> Translate;
  };
  
  extern bool fPrintToConsole;
  extern bool fPrintToDebugLog;
  
  extern bool fLogTimestamps;
  extern bool fLogTimeMicros;
  extern bool fLogThreadNames;
  extern bool fLogIPs;
  extern std::atomic<bool> fReopenDebugLog;
  extern CTranslationInterface translationInterface;
  
  extern const char * const BITCOIN_CONF_FILENAME;
  extern const char * const BITCOIN_PID_FILENAME;
  
  extern std::atomic<uint64_t> logCategories;
  
  /**
   * Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
   * If no translation slot is registered, nothing is returned, and simply return the input.
   */
  inline std::string _(const char* psz)
  {
      boost::optional<std::string> rv = translationInterface.Translate(psz);
      return rv ? (*rv) : psz;
  }
  
  void SetupEnvironment();
  bool SetupNetworking();
  
  struct CLogCategoryActive
  {
      std::string category;
      bool active;
  };
  
  namespace BCLog {
      enum LogFlags : uint64_t {
          NONE        = 0,
          NET         = (1 <<  0),
          TOR         = (1 <<  1),
          MEMPOOL     = (1 <<  2),
          HTTP        = (1 <<  3),
          BENCHMARK   = (1 <<  4),
          ZMQ         = (1 <<  5),
          DB          = (1 <<  6),
          RPC         = (1 <<  7),
          ESTIMATEFEE = (1 <<  8),
          ADDRMAN     = (1 <<  9),
          SELECTCOINS = (1 << 10),
          REINDEX     = (1 << 11),
          CMPCTBLOCK  = (1 << 12),
          RANDOM      = (1 << 13),
          PRUNE       = (1 << 14),
          PROXY       = (1 << 15),
          MEMPOOLREJ  = (1 << 16),
          LIBEVENT    = (1 << 17),
          COINDB      = (1 << 18),
          QT          = (1 << 19),
          LEVELDB     = (1 << 20),
  
          //Start Jokecoin
          CHAINLOCKS  = ((uint64_t)1 << 32),
          GOBJECT     = ((uint64_t)1 << 33),
          INSTANTSEND = ((uint64_t)1 << 34),
          KEEPASS     = ((uint64_t)1 << 35),
          LLMQ        = ((uint64_t)1 << 36),
          LLMQ_DKG    = ((uint64_t)1 << 37),
          LLMQ_SIGS   = ((uint64_t)1 << 38),
          MNPAYMENTS  = ((uint64_t)1 << 39),
          MNSYNC      = ((uint64_t)1 << 40),
          PRIVATESEND = ((uint64_t)1 << 41),
          SPORK       = ((uint64_t)1 << 42),
          NETCONN     = ((uint64_t)1 << 43),
          //End Jokecoin
  
          NET_NETCONN = NET | NETCONN, // use this to have something logged in NET and NETCONN as well
  
          ALL         = ~(uint64_t)0,
      };
  }
  static inline bool LogAcceptCategory(uint64_t category)
  {
      return (logCategories.load(std::memory_order_relaxed) & category) != 0;
  }
  
  /** Returns a string with the log categories. */
  std::string ListLogCategories();
  
  /** Returns a string with the list of active log categories */
  std::string ListActiveLogCategoriesString();
  
  /** Returns a vector of the active log categories. */
  std::vector<CLogCategoryActive> ListActiveLogCategories();
  
  /** Return true if str parses as a log category and set the flags in f */
  bool GetLogCategory(uint64_t *f, const std::string *str);
  
  /** Send a string to the log output */
  int LogPrintStr(const std::string &str);
  
  /** Formats a string without throwing exceptions. Instead, it'll return an error string instead of formatted string. */
  template<typename... Args>
  std::string SafeStringFormat(const std::string& fmt, const Args&... args)
  {
      try {
          return tinyformat::format(fmt, args...);
      } catch (std::runtime_error& fmterr) {
          std::string message = tinyformat::format("\n****TINYFORMAT ERROR****\n    err=\"%s\"\n    fmt=\"%s\"\n", fmterr.what(), fmt);
          fprintf(stderr, "%s", message.c_str());
          return message;
      }
  }
  
  /** Get format string from VA_ARGS for error reporting */
  template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; }
  
  static inline void MarkUsed() {}
  template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args)
  {
      (void)t;
      MarkUsed(args...);
  }
  
  // Be conservative when using LogPrintf/error or other things which
  // unconditionally log to debug.log! It should not be the case that an inbound
  // peer can fill up a users disk with debug.log entries.
  
  #ifdef USE_COVERAGE
  #define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
  #define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
  #else
  #define LogPrintf(...) do { \
      std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
      try { \
          _log_msg_ = tfm::format(__VA_ARGS__); \
      } catch (tinyformat::format_error &e) { \
          /* Original format string will have newline so don't add one here */ \
          _log_msg_ = "Error \"" + std::string(e.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \
      } \
      LogPrintStr(_log_msg_); \
  } while(0)
  
  #define LogPrint(category, ...) do { \
      if (LogAcceptCategory((category))) { \
          LogPrintf(__VA_ARGS__); \
      } \
  } while(0)
  #endif
  
  template<typename... Args>
  bool error(const char* fmt, const Args&... args)
  {
      LogPrintStr("ERROR: " + SafeStringFormat(fmt, args...) + "\n");
      return false;
  }
  
  void PrintExceptionContinue(const std::exception_ptr pex, const char* pszExceptionOrigin);
  void FileCommit(FILE *file);
  bool TruncateFile(FILE *file, unsigned int length);
  int RaiseFileDescriptorLimit(int nMinFD);
  void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
  bool RenameOver(fs::path src, fs::path dest);
  bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
  
  /** Release all directory locks. This is used for unit testing only, at runtime
   * the global destructor will take care of the locks.
   */
  void ReleaseDirectoryLocks();
  
  bool TryCreateDirectories(const fs::path& p);
  fs::path GetDefaultDataDir();
  const fs::path &GetDataDir(bool fNetSpecific = true);
  fs::path GetBackupsDir();
  void ClearDatadirCache();
  fs::path GetConfigFile(const std::string& confPath);
  #ifndef WIN32
  fs::path GetPidFile();
  void CreatePidFile(const fs::path &path, pid_t pid);
  #endif
  #ifdef WIN32
  fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
  #endif
  fs::path GetDebugLogPath();
  bool OpenDebugLog();
  void ShrinkDebugFile();
  void runCommand(const std::string& strCommand);
  
  /**
   * Most paths passed as configuration arguments are treated as relative to
   * the datadir if they are not absolute.
   *
   * @param path The path to be conditionally prefixed with datadir.
   * @param net_specific Forwarded to GetDataDir().
   * @return The normalized path.
   */
  fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true);
  
  inline bool IsSwitchChar(char c)
  {
  #ifdef WIN32
      return c == '-' || c == '/';
  #else
      return c == '-';
  #endif
  }
  
  class ArgsManager
  {
  protected:
      friend class ArgsManagerHelper;
  
      mutable CCriticalSection cs_args;
      std::map<std::string, std::vector<std::string>> m_override_args;
      std::map<std::string, std::vector<std::string>> m_config_args;
      std::string m_network;
      std::set<std::string> m_network_only_args;
  
      void ReadConfigStream(std::istream& stream);
  
  public:
      ArgsManager();
  
      /**
       * Select the network in use
       */
      void SelectConfigNetwork(const std::string& network);
  
      void ParseParameters(int argc, const char*const argv[]);
      void ReadConfigFile(const std::string& confPath);
  
      /**
       * Log warnings for options in m_section_only_args when
       * they are specified in the default section but not overridden
       * on the command line or in a network-specific section in the
       * config file.
       */
      void WarnForSectionOnlyArgs();
  
      /**
       * Return a vector of strings of the given argument
       *
       * @param strArg Argument to get (e.g. "-foo")
       * @return command-line arguments
       */
      std::vector<std::string> GetArgs(const std::string& strArg) const;
  
      /**
       * Return true if the given argument has been manually set
       *
       * @param strArg Argument to get (e.g. "-foo")
       * @return true if the argument has been set
       */
      bool IsArgSet(const std::string& strArg) const;
  
      /**
       * Return true if the argument was originally passed as a negated option,
       * i.e. -nofoo.
       *
       * @param strArg Argument to get (e.g. "-foo")
       * @return true if the argument was passed negated
       */
      bool IsArgNegated(const std::string& strArg) const;
  
      /**
       * Return string argument or default value
       *
       * @param strArg Argument to get (e.g. "-foo")
       * @param strDefault (e.g. "1")
       * @return command-line argument or default value
       */
      std::string GetArg(const std::string& strArg, const std::string& strDefault) const;
  
      /**
       * Return integer argument or default value
       *
       * @param strArg Argument to get (e.g. "-foo")
       * @param nDefault (e.g. 1)
       * @return command-line argument (0 if invalid number) or default value
       */
      int64_t GetArg(const std::string& strArg, int64_t nDefault) const;
  
      /**
       * Return boolean argument or default value
       *
       * @param strArg Argument to get (e.g. "-foo")
       * @param fDefault (true or false)
       * @return command-line argument or default value
       */
      bool GetBoolArg(const std::string& strArg, bool fDefault) const;
  
      /**
       * Set an argument if it doesn't already have a value
       *
       * @param strArg Argument to set (e.g. "-foo")
       * @param strValue Value (e.g. "1")
       * @return true if argument gets set, false if it already had a value
       */
      bool SoftSetArg(const std::string& strArg, const std::string& strValue);
  
      /**
       * Set a boolean argument if it doesn't already have a value
       *
       * @param strArg Argument to set (e.g. "-foo")
       * @param fValue Value (e.g. false)
       * @return true if argument gets set, false if it already had a value
       */
      bool SoftSetBoolArg(const std::string& strArg, bool fValue);
  
      // Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
      // been set. Also called directly in testing.
      void ForceSetArg(const std::string& strArg, const std::string& strValue);
      void ForceRemoveArg(const std::string& strArg);
  
      /**
       * Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
       * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given.
       */
      std::string GetChainName() const;
  
      /**
       * Looks for -devnet and returns either "devnet-<name>" or simply "devnet" if no name was specified.
       * This function should never be called for non-devnets.
       * @return either "devnet-<name>" or "devnet"; raises runtime error if no -devent was specified.
       */
      std::string GetDevNetName() const;
  };
  
  extern ArgsManager gArgs;
  
  /**
   * Format a string to be used as group of options in help messages
   *
   * @param message Group name (e.g. "RPC server options:")
   * @return the formatted string
   */
  std::string HelpMessageGroup(const std::string& message);
  
  /**
   * Format a string to be used as option description in help messages
   *
   * @param option Option message (e.g. "-rpcuser=<user>")
   * @param message Option description (e.g. "Username for JSON-RPC connections")
   * @return the formatted string
   */
  std::string HelpMessageOpt(const std::string& option, const std::string& message);
  
  /**
   * Return the number of physical cores available on the current system.
   * @note This does not count virtual cores, such as those provided by HyperThreading
   * when boost is newer than 1.56.
   */
  int GetNumCores();
  
  void RenameThread(const char* name);
  std::string GetThreadName();
  
  namespace ctpl {
      class thread_pool;
  }
  void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName);
  
  /**
   * .. and a wrapper that just calls func once
   */
  template <typename Callable> void TraceThread(const std::string name,  Callable func)
  {
      std::string s = "jokecoin-" + name;
      RenameThread(s.c_str());
      try
      {
          LogPrintf("%s thread start\n", name);
          func();
          LogPrintf("%s thread exit\n", name);
      }
      catch (const boost::thread_interrupted&)
      {
          LogPrintf("%s thread interrupt\n", name);
          throw;
      }
      catch (...) {
          PrintExceptionContinue(std::current_exception(), name.c_str());
          throw;
      }
  }
  
  std::string CopyrightHolders(const std::string& strPrefix, unsigned int nStartYear, unsigned int nEndYear);
  
  /**
   * @brief Converts version strings to 4-byte unsigned integer
   * @param strVersion version in "x.x.x" format (decimal digits only)
   * @return 4-byte unsigned integer, most significant byte is always 0
   * Throws std::bad_cast if format doesn\t match.
   */
  uint32_t StringVersionToInt(const std::string& strVersion);
  
  
  /**
   * @brief Converts version as 4-byte unsigned integer to string
   * @param nVersion 4-byte unsigned integer, most significant byte is always 0
   * @return version string in "x.x.x" format (last 3 bytes as version parts)
   * Throws std::bad_cast if format doesn\t match.
   */
  std::string IntVersionToString(uint32_t nVersion);
  
  
  /**
   * @brief Copy of the IntVersionToString, that returns "Invalid version" string
   * instead of throwing std::bad_cast
   * @param nVersion 4-byte unsigned integer, most significant byte is always 0
   * @return version string in "x.x.x" format (last 3 bytes as version parts)
   * or "Invalid version" if can't cast the given value
   */
  std::string SafeIntVersionToString(uint32_t nVersion);
  
  
  //! Substitute for C++14 std::make_unique.
  template <typename T, typename... Args>
  std::unique_ptr<T> MakeUnique(Args&&... args)
  {
      return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
  }
  
  #endif // BITCOIN_UTIL_H
  