/** @file
    WCCP utilities and logging.

    @section license License

    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 <vector>

namespace wccp
{
/// @name Message severity levels.
//@{
extern ts::Errata::Code LVL_FATAL; ///< Fatal, cannot continue.
extern ts::Errata::Code LVL_WARN;  ///< Significant, function degraded.
extern ts::Errata::Code LVL_INFO;  ///< Interesting, not necessarily a problem.
extern ts::Errata::Code LVL_DEBUG; ///< Debugging information.
extern ts::Errata::Code LVL_TMP;   ///< For temporary debugging only.
                                   // Handy so that temporary debugging messages can be located by grep.
                                   //@}

/** Logging / reporting support.
    We build on top of @c Errata but we want to be able to prevent
    message generation / allocation for messages with severity levels
    less than a run time controllable value.

    @internal Far from complete but serving as a prototype / experiment
    to learn what's actually useful.
*/
//@{
/// Report literal string to an Errata.
/// @return @a err.
ts::Errata &log(ts::Errata &err,       ///< Target errata.
                ts::Errata::Id id,     ///< Message ID.
                ts::Errata::Code code, ///< Severity level.
                char const *text       ///< Message text.
                );
/// Report literal string to an Errata.
/// Use message ID 0.
/// @return @a err.
ts::Errata &log(ts::Errata &err,       ///< Target errata.
                ts::Errata::Code code, ///< Severity level.
                char const *text       ///< Message text.
                );
/// printf style log to Errata.
/// @return @a err.
ts::Errata &logf(ts::Errata &err,       ///< Target errata.
                 ts::Errata::Id id,     ///< Message ID.
                 ts::Errata::Code code, ///< Severity level.
                 char const *format,    ///< Format string.
                 ...                    ///< Format string parameters.
                 );
/// printf style log to Errata.
/// The message id is set to zero.
/// @return @a err.
ts::Errata &logf(ts::Errata &err,       ///< Target errata.
                 ts::Errata::Code code, ///< Severity level.
                 char const *format,    ///< Format string.
                 ...                    ///< Format string parameters.
                 );
/// Return an Errata populated with a literal string.
/// Use message ID 0.
/// @return @a err.
ts::Errata log(ts::Errata::Code code, ///< Severity level.
               char const *text       ///< Message text.
               );
/// Return an Errata populated with a printf styleformatted string.
/// Use message ID 0.
/// @return @a err.
ts::Errata logf(ts::Errata::Code code, ///< Severity level.
                char const *format,    ///< Message text.
                ...);
/** Return an Errata based on @c errno.
    The literal string is combined with the system text for the current
    value of @c errno. This is modeled on @c perror. Message ID 0 is used.
    @return @a err.
 */
ts::Errata log_errno(ts::Errata::Code code, ///< Severity level.
                     char const *text       ///< Message text.
                     );
/** Return an @c Errata based on @c errno.
    @c errno and the corresponding system error string are appended to
    the results from the @a format and following arguments.
 */
ts::Errata logf_errno(ts::Errata::Code code, ///< Severity code.
                      char const *format,    ///< Format string.
                      ...                    ///< Arguments for @a format.
                      );
//@}

// ------------------------------------------------------
/*  Template support for access to raw message fields.
    We have three overloads, one for each size of field.
    By passing the member pointer we avoid having to specify
    any template argument at the call site. For instance, to
    get a 16 bit field out of a "raw_t" struct with a member
    name of "field", you would use

    get_field(&raw_t::field, buffer);

    Note these templates handle network ordering internally.
    See access_field for completely unmediated access.
*/

template <typename R>
uint8_t
get_field(uint8_t R::*M, char *buffer)
{
  return reinterpret_cast<R *>(buffer)->*M;
}

template <typename R>
uint16_t
get_field(uint16_t R::*M, char *buffer)
{
  return ntohs(reinterpret_cast<R *>(buffer)->*M);
}

template <typename R>
uint32_t
get_field(uint32_t R::*M, char *buffer)
{
  return ntohl(reinterpret_cast<R *>(buffer)->*M);
}

template <typename R>
void
set_field(uint16_t R::*M, char *buffer, uint16_t value)
{
  reinterpret_cast<R *>(buffer)->*M = htons(value);
}

template <typename R>
void
set_field(uint32_t R::*M, char *buffer, uint32_t value)
{
  reinterpret_cast<R *>(buffer)->*M = htonl(value);
}

// IP address fields are kept locally in network order so
// we need variants without re-ordering.
// Also required for member structures.
template <typename R, typename T>
T &
access_field(T R::*M, void *buffer)
{
  return reinterpret_cast<R *>(buffer)->*M;
}

// Access an array @a T starting at @a buffer.
template <typename T>
T *
access_array(void *buffer)
{
  return reinterpret_cast<T *>(buffer);
}
// Access an array of @a T starting at @a buffer.
template <typename T>
T const *
access_array(void const *buffer)
{
  return reinterpret_cast<T const *>(buffer);
}

/// Find an element in a vector by the value of a member.
/// @return An iterator for the element, or equal to @c end if not found.
template <typename T, ///< Vector element type.
          typename V  ///< Member type.
          >
typename std::vector<T>::iterator
find_by_member(std::vector<T> &container, ///< Vector with elements.
               V T::*member,              ///< Pointer to member to compare.
               V const &value             ///< Value to match.
               )
{
  typename std::vector<T>::iterator spot  = container.begin();
  typename std::vector<T>::iterator limit = container.end();
  while (spot != limit && (*spot).*member != value)
    ++spot;
  return spot;
}
// ------------------------------------------------------
/// Find a non-loop back IP address from an open socket.
uint32_t Get_Local_Address(int s ///< Open socket.
                           );
// ------------------------------------------------------
/// Cheap and dirty conversion to string for debugging.
/// @note Uses a static buffer so won't work across threads or
/// twice in the same argument list.
char const *ip_addr_to_str(uint32_t addr ///< Address to convert.
                           );

/** Used for printing IP address.
    @code
    uint32_t addr; // IP address.
    printf("IP address = " ATS_IP_PRINTF_CODE,ATS_IP_OCTETS(addr));
    @endcode
 */
#define ATS_IP_PRINTF_CODE "%d.%d.%d.%d"
#define ATS_IP_OCTETS(x)                                                                              \
  reinterpret_cast<unsigned char const *>(&(x))[0], reinterpret_cast<unsigned char const *>(&(x))[1], \
    reinterpret_cast<unsigned char const *>(&(x))[2], reinterpret_cast<unsigned char const *>(&(x))[3]
// ------------------------------------------------------

} // namespace Wccp
