/**
 * @copyright
 * ====================================================================
 *    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.
 * ====================================================================
 * @endcopyright
 */

#ifndef __cplusplus
#error "This is a C++ header file."
#endif

#ifndef SVN_CXXHL_EXCEPTION_HPP
#define SVN_CXXHL_EXCEPTION_HPP

#include <exception>
#include <string>
#include <utility>
#include <vector>

#include "svncxxhl/_compat.hpp"

namespace apache {
namespace subversion {
namespace cxxhl {

namespace compat {} // Announce the compat namespace for shared_ptr lookup

namespace detail {
// Forward declaration of implementation-specific structure
class ErrorDescription;
} // namespace detail

/**
 * Exceptions generated by the C++HL implementation.
 */
class InternalError : public std::exception
{
public:
  explicit InternalError(const char* description);

  InternalError(const InternalError& that) throw();
  InternalError& operator= (const InternalError& that) throw();
  virtual ~InternalError() throw();

  /**
   * Returns the message associated with this exception object.
   */
  virtual const char* what() const throw();

protected:
  typedef compat::shared_ptr<detail::ErrorDescription> description_ptr;
  explicit InternalError(description_ptr description) throw();
  description_ptr m_description;
};

/**
 * Encapsulate a stack of Subversion error codes and messages.
 */
class Error : public InternalError
{
public:
  Error(const Error& that) throw();
  Error& operator=(const Error& that) throw();
  virtual ~Error() throw();

  /**
   * Returns the error code associated with the top-level error that
   * caused the exception.
   */
  virtual int code() const throw();

  /**
   * Error message description.
   */
  class Message
  {
  public:
    /**
     * Create a message object given an error code and error message.
     */
    Message(int errval, const std::string& message)
      : m_errno(errval),
        m_message(message),
        m_trace(false)
      {}

    /**
     * Create a message object given an error code and error message,
     * and set the flag that tells if this is a debugging traceback entry.
     */
    Message(int errval, const std::string& message, bool trace)
      : m_errno(errval),
        m_message(message),
        m_trace(trace)
      {}

    /**
     * Return the error code.
     */
    int code() const throw() { return m_errno; }

    /**
     * Return the error message.
     */
    const std::string& message() const throw() { return m_message; }

    /**
     * Return the generic error message associated with the error code.
     */
    const char* generic_message() const;

    /**
     * Check if this message is in fact a debugging traceback entry.
     */
    bool trace() const throw() { return m_trace; }

  private:
    int m_errno;
    std::string m_message;
    bool m_trace;
  };

  /**
   * The list of messages associated with an error.
   */
  typedef std::vector<Message> MessageList;

  /**
   * Returns the complete list of error messages, including those from
   * nested errors.
   */
  virtual MessageList messages() const
    {
      return compile_messages(false);
    }

  /**
   * Like error::messages(), but includes debugging traceback.
   *
   * @note
   * Traceback is only available if the Subversion libraries were
   * compiled with tracing enabled.
   */
  virtual MessageList traced_messages() const
    {
      return compile_messages(true);
    }

protected:
  explicit Error(description_ptr description) throw()
    : InternalError(description)
    {}
  MessageList compile_messages(bool show_traces) const;
};

/**
 * Thrown instead of Error when the error chain contains a
 * @c SVN_ERR_CANCELLED error code.
 */
class Cancelled : public Error
{
protected:
  explicit Cancelled(description_ptr description) throw()
    : Error(description)
    {}
};

} // namespace cxxhl
} // namespace subversion
} // namespace apache

#endif  // SVN_CXXHL_EXCEPTION_HPP
