/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 "geode_defs.hpp"
#include "begin_native.hpp"
#include "util/Log.hpp"
#include "end_native.hpp"



using namespace System;

namespace Apache
{
  namespace Geode
  {
    namespace Client
    {

      /// <summary>
      /// Logging levels.
      /// </summary>
      public enum class LogLevel
      {
        /// <summary>
        /// No log.
        /// </summary>
        Null = 0,

        /// <summary>
        /// Indicates serious failure.
        /// </summary>
        Error,
        /// <summary>
        /// Indicates potential problem.
        /// </summary>
        Warning,
        /// <summary>
        /// For informational purpose.
        /// </summary>
        Info,

        /// <summary>
        /// The default logging level.
        /// </summary>
        Default,

        /// <summary>
        /// For Static configuration messages.
        /// </summary>
        Config,

        /// <summary>
        /// For tracing information.
        /// </summary>
        Fine,
        /// <summary>
        /// For moderately detailed tracing information.
        /// </summary>
        Finer,
        /// <summary>
        /// For very detailed tracing information.
        /// </summary>
        Finest,

        /// <summary>
        /// For highly detailed tracing information.
        /// </summary>
        Debug,

        /// <summary>
        /// All the log messages.
        /// </summary>
        All,
      };


      /// <summary>
      /// Defines methods available to clients that want to write a log message
      /// to their Geode system's shared log file.
      /// </summary>
      /// <remarks>
      /// Any attempt to use an instance after its connection is disconnected
      /// will throw a <c>NotConnectedException</c>.
      /// <para>
      /// For any logged message the log file will contain:
      /// <ul>
      /// <li> The message's log level.</li>
      /// <li> The time the message was logged.</li>
      /// <li> The ID of the connection and thread that logged the message.</li>
      /// <li> The message itself, perhaps with
      /// an exception including the exception's stack trace.</li>
      /// </ul>
      /// </para><para>
      /// A message always has a level.
      /// Logging levels are ordered. Enabling logging at a given level also
      /// enables logging at higher levels. The higher the level the more
      /// important and urgent the message.
      /// </para><para>
      /// The levels, in descending order of severity, are:
      /// <ul>
      ///
      /// <li> <c>Error</c> (highest severity) is a message level
      /// indicating a serious failure.  In general <c>error</c>
      /// messages should describe events that are of considerable
      /// importance and which will prevent normal program execution. They
      /// should be reasonably intelligible to end users and to system
      /// administrators.</li>
      ///
      /// <li> <c>Warning</c> is a message level indicating a
      /// potential problem.  In general <c>warning</c> messages
      /// should describe events that will be of interest to end users or
      /// system managers, or which indicate potential problems.</li>
      ///
      /// <li> <c>Info</c> is a message level for informational
      /// messages.  Typically <c>info</c> messages should be
      /// reasonably significant and should make sense to end users and
      /// system administrators.</li>
      ///
      /// <li> <c>Config</c> is a message level for static
      /// configuration messages.  <c>config</c> messages are intended
      /// to provide a variety of static configuration information, to
      /// assist in debugging problems that may be associated with
      /// particular configurations.</li>
      ///
      /// <li> <c>Fine</c> is a message level providing tracing
      /// information.  In general the <c>fine</c> level should be
      /// used for information that will be broadly interesting to
      /// developers. This level is for the lowest volume, and most
      /// important, tracing messages.</li>
      ///
      /// <li> <c>Finer</c> indicates a moderately detailed tracing
      /// message.  This is an intermediate level between <c>fine</c>
      /// and <c>finest</c>.</li>
      ///
      /// <li> <c>Finest</c> indicates a very detailed tracing
      /// message.  Logging calls for entering, returning, or throwing an
      /// exception are traced at the <c>finest</c> level.</li>
      ///
      /// <li> <c>Debug</c> (lowest severity) indicates a highly
      /// detailed tracing message.  In general the <c>debug</c> level
      /// should be used for the most voluminous detailed tracing messages.</li>
      /// </ul>
      ///
      /// </para>
      /// </remarks>
      private ref class Log STATICCLASS
      {
      public:



        /// <summary>
        /// Initializes the logging facility with the given level and filename.
        /// </summary>
        /// <param name="level">the logging level</param>
        /// <param name="logFileName">the log file name</param>
        static void Init(LogLevel level, String^ logFileName);

        /// <summary>
        /// Initializes logging facility with given level, filename, and file size limit.
        /// </summary>
        /// <param name="level">the logging level</param>
        /// <param name="logFileName">the log file name</param>
        /// <param name="logFileLimit">maximum allowable size of the log file, in bytes, 
        ///        or 0 for the default (1 Gbyte)</param>
        static void Init(LogLevel level, String^ logFileName, System::Int32 logFileLimit);

        /// <summary>
        /// Closes logging facility (until next init).
        /// </summary>
        static void Close();

        /// <summary>
        /// Returns the current log level.
        /// </summary>
        static LogLevel Level();

        /// <summary>
        /// Sets the current log level.
        /// </summary>
        static void SetLevel(LogLevel level);

        /// <summary>
        /// True if log messages at the given level are enabled.
        /// </summary>
        static bool Enabled(LogLevel level);

        /// <summary>
        /// Logs a message at the given level.
        /// </summary>
        static void Write(LogLevel level, String^ msg);

        /// <summary>
        /// Logs both a message and a thrown exception.
        /// </summary>
        static void LogThrow(LogLevel level, String^ msg, System::Exception^ ex);

        /// <summary>
        /// Logs both a message and a caught exception.
        /// </summary>
        static void LogCatch(LogLevel level, String^ msg, System::Exception^ ex);

        // Convenience functions with variable number of arguments
        // as in String.Format

        /// <summary>
        /// Error level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Error(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Error)
            Log::Write(LogLevel::Error, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Warning level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Warning(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Warning)
            Log::Write(LogLevel::Warning, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Info level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Info(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Info)
            Log::Write(LogLevel::Info, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Config level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Config(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Config)
            Log::Write(LogLevel::Config, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Fine level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Fine(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Fine)
            Log::Write(LogLevel::Fine, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Finer level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Finer(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Finer)
            Log::Write(LogLevel::Finer, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Finest level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Finest(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Finest)
            Log::Write(LogLevel::Finest, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }

        /// <summary>
        /// Debug level logging with variable number of arguments using
        /// format as in <c>System.String.Format</c>.
        /// </summary>
        inline static void Debug(String^ format, ... array<Object^>^ args)
        {
          if (staticLogLevel >= LogLevel::Debug)
            Log::Write(LogLevel::Debug, String::Format(
            System::Globalization::CultureInfo::CurrentCulture, format, args));
        }
      internal:

        static void SetLogLevel(LogLevel level)
        {
          staticLogLevel = level;
        }

      private:
        static LogLevel staticLogLevel = LogLevel::Null;
      };
    }  // namespace Client
  }  // namespace Geode
}  // namespace Apache

