/*=========================================================================

  Program: GDCM (Grassroots DICOM). A DICOM library

  Copyright (c) 2006-2011 Mathieu Malaterre
  All rights reserved.
  See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#ifndef GDCMEXCEPTION_H
#define GDCMEXCEPTION_H

#include <cassert>
#include <cstdlib> // NULL
#include <exception>
#include <sstream> // ostringstream
#include <stdexcept> // logic_error
#include <string>

// Disable clang warning "dynamic exception specifications are deprecated".
// We need to be C++03 and C++11 compatible, and if we remove the 'throw()'
// specifier we'll get an error in C++03 by not matching the superclass.
#if defined(__clang__) && defined(__has_warning)
# if __has_warning("-Wdeprecated")
#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wdeprecated"
# endif
#endif

#define gdcm_forced_assert(cond) assert(cond)

namespace gdcm
{

/**
 * \brief Exception
 * \details Standard exception handling object.
 * \note Its copy-constructor and assignment operator are generated by the
 * compiler.
 */
class Exception : public std::exception
{
  /// std::logic_error is used to internally hold a string.
  /// It has the nice property of having a copy-constructor
  /// that never fails. Thereby it provides the same guarantee to
  /// the copy-constructor of gdcm::Exception.
  typedef std::logic_error StringHolder;

  /// Creates the text to be returned by Exception:what().
  static StringHolder CreateWhat(const char* const desc,
                                 const char* const file,
                                 const unsigned int lineNumber,
                                 const char* const func)
  {
    gdcm_forced_assert(desc != nullptr);
    gdcm_forced_assert(file != nullptr);
    gdcm_forced_assert(func != nullptr);
    std::ostringstream oswhat;
    oswhat << file << ":" << lineNumber << " (" << func << "):\n";
    oswhat << desc;
    return StringHolder( oswhat.str() );
  }

public:
  /// Explicit constructor, initializing the description and the
  /// text returned by what().
  /// \note The last parameter is ignored for the time being.
  /// It may be used to specify the function where the exception
  /// was thrown.
  explicit Exception(const char *desc = "None",
            const char *file = __FILE__,
            unsigned int lineNumber = __LINE__,
            // FIXME:  __PRETTY_FUNCTION__ is the non-mangled version for __GNUC__ compiler
            const char *func = "" /*__FUNCTION__*/)
  :
  What( CreateWhat(desc, file, lineNumber, func) ),
  Description(desc)
  {
  }

  ~Exception() throw() override {}

  /// what implementation
  const char* what() const throw() override
    {
    return What.what();
    }

  /// Return the Description
  const char * GetDescription() const { return Description.what(); }

private:
  StringHolder  What;
  StringHolder  Description;
};

} // end namespace gdcm

// Always defined
#define gdcm_assert(cond) \
  if (!(cond)) throw gdcm::Exception("An invalid logic behavior occurred" #cond, __FILE__ , __LINE__)

/* Asserts that should only exist in debug builds. */
#ifndef NDEBUG // checks in debug builds and elision in release builds (like assert)
#define gdcm_debug_assert(cond) gdcm_assert(cond)
#else
#define gdcm_debug_assert(cond) ((void)0)
#endif

// Undo warning suppression.
#if defined(__clang__) && defined(__has_warning)
# if __has_warning("-Wdeprecated")
#  pragma clang diagnostic pop
# endif
#endif

#endif
