#ifndef ERROR_MAP_HXX
#define ERROR_MAP_HXX

// @<COPYRIGHT_START>@
// ===============================================
// Copyright 2006 UGS Corp. All Rights Reserved.
// ===============================================
// @<COPYRIGHT_END>@

/**
    @file

    Set-based error reporting and enquiry.
    Set-based functions report errors in two different ways.

    1.  *Hard* errors - throw an IFail object.
      Execution is passed rapidly back to an enclosing catch block.
      Any returned objects can, and should be, destroyed as usual.  No other guarantee is made about program state.
      An error is hard if:
        - You could (in some valid implementation) find the same error on an empty-set input.
        - The error has nothing to do with the current set member (array index), or any closely related object.
        - It would be insane to continue execution.

    2.  *Soft* errors - return an ErrorMap object.
      Processing of the current set member is aborted, and iteration over the next set member will begin.
      There must be no observable state change for the aborted set member.
      An error is soft if:
        - There are no side effects from the current iteration, or the side effects have been undone.
        - The error is associated with an individual set member (array index), or a closely related object.
        - It is sensible to continue execution.
*/

/**
\page error_map_exception_policy ErrorMap exception policy
Error map additions may throw an IFail exception rather than return,
depending on the current exception policy.
See class Teamcenter::Configuration::ExceptionPolicy for details.
*/

#include <base_utils/SharedPtr.hxx>
#include <base_utils/TcBaseTypes.hxx>
#include <tc/tc_startup.h>
#include <base_utils/ErrorStoreBase.hxx>
#include <error.h>

class IFail;
class ResultStatus;
class status_t;

#include <fclasses/libfclasses_exports.h>
// \ref doxygen_namespace_bug
namespace Teamcenter
{
class Error;
class ExceptionPolicy;

// Implementation details.
namespace Private
{
/// \brief Take ownership of error stack into an Error object.
/// *Clear* the error stack ready for later use.
/// \ref error_map_exception_policy
FCLASSES_API Error takeErrorStack( int ifail );
/// \brief Take ownership of error stack into an Error object, and *clear*.
/// *Clear* the error stack ready for later use.
/// \ref error_map_exception_policy
FCLASSES_API Error takeErrorStack( IFail const& ifail );
/// \brief Take ownership of error stack into an Error object, and *clear*.
/// *Clear* the error stack ready for later use.
/// \ref error_map_exception_policy
FCLASSES_API Error takeErrorStack( status_t const& status );

/// \brief Read error stack into an Error object.
/// *Retain* the error stack.
/// \ref error_map_exception_policy
FCLASSES_API Error readErrorStack( int ifail );

/// \brief Push ifail to error stach and create an Error object.
/// *Retain* the error stack.
/// \ref error_map_exception_policy
FCLASSES_API Error pushErrorStack( int ifail, const char *s1=0, const char *s2=0, const char *s3=0, const char *s4=0, const char *s5=0, const char *s6=0, const char *s7=0 );
}

/**
\brief The most-recent error, and related information.
If required, we may turn this into an iterable error list, starting with the most recent.

If a function returns an Error, this can be tested for goodness in ResultStatus style...
sample: \code
// Declare function
Error func1( argdecls... );
try
{
    // Call function returning an Error.
    ResultStatus ithrow = func1( args... );
}
catch ( Ifail& )
{
    // Handle error.
}
\endcode

\note
Default constructor, copy and assign required for std container insertion.
*/
class FCLASSES_API Error
{
public:
    /// ITK_ok "error".
    inline Error();
    inline ~Error();
    // Default copy and assign.
    // Error( Error const& );
    // Error& operator=( Error const& );

    /// Is the most-recent ifail ITK_ok?
    inline bool isOk() const;
    /// Is the most-recent ifail not ITK_ok?
    inline bool isError() const;

    /// Integer ifail, errorMessage.
    inline int ifail() const;
    inline std::string errorMessage() const;
    /// Throw IFail if not ITK_ok.
    operator ResultStatus() const;

private:
    friend Error Teamcenter::Private::takeErrorStack( int ifail );
    friend Error Teamcenter::Private::takeErrorStack( IFail const& ifail );
    friend Error Teamcenter::Private::takeErrorStack( status_t const& status );

    friend Error Teamcenter::Private::readErrorStack( int ifail );
    friend Error Teamcenter::Private::pushErrorStack( int ifail, const char *s1, const char *s2, const char *s3, const char *s4, const char *s5, const char *s6, const char *s7 );

    /// Construct with ifail value, errorMessage.
    inline explicit Error( int ifail );

    /// Error data, cf. EMH_ask_errors.
    int m_ifail;
    std::string m_errorMessage;
};

/**
\brief An indexed map of errors.
Filters out ITK_ok errors on addition.
ErrorMap implements a set-based style of error reporting, very different
from Teamcenter ITK-style error stacks.  We expect clients to return
ErrorMaps from set-based functions, and the class is optimized for this use case.
Clietns will associate each Error with an array index identifier.  The ErrorMap class
_conceptually_ stacks up Errors under these index identifiers, returning the most recent.
Clients may need to re-map index identifiers as they pass ErrorMaps up the call chain.

sample: \code
ErrorMap errors;
errors += func1( args... );
errors += func2( args... );
return errors;
\endcode

\note A std::map<size_t, Error> almost does the trick, however we want to introduce some
new functions of our own.  Since we should not derive from std::map, let's wrap it instead.
*/
class FCLASSES_API ErrorMap
{
public:
    /// Empty set - no errors.
    inline ErrorMap();

    // Copy and assign.
    inline ErrorMap( ErrorMap const& );
    inline ErrorMap& operator=( ErrorMap const& );

    /// \brief Add an individual error to the error set.
    /// Discard all but the most recent error.
    ErrorMap& add( size_t id, Error const& error );
    /// \brief Add an individual integer fail code to the error set.
    /// Handle the ifail by taking ownership of (*clearing*) the current error stack.
    inline ErrorMap& add( size_t id, int ifail );
    /// \brief Add an individual IFail to the error set.
    /// Handle the ifail by taking ownership of (*clearing*) the current error stack.
    inline ErrorMap& add( size_t id, IFail const& ifail );
    /// \brief Add an individual status_t to the error set.
    /// Handle the ifail by taking ownership of (*clearing*) the current error stack.
    inline ErrorMap& add( size_t id, status_t const& status );
    /// \brief Update with the latest errors.
    /// More recent (right) errors, replace older (this) errors.
    ErrorMap& operator+=( ErrorMap const& right );

    /// stl-like typedefs.
    typedef size_t key_type;
    typedef Error mapped_type;
    typedef StdSmMap< size_t, Error >::Map::value_type value_type;

    /// stl-like iterators.
    typedef StdSmMap< size_t, Error >::Map::iterator iterator;
    typedef StdSmMap< size_t, Error >::Map::const_iterator const_iterator;

    /// stl-like map size.
    inline size_t size() const;
    /// stl-like is map empty?
    inline bool empty() const;

    /// stl-like map lookup.
    iterator find( size_t id );
    const_iterator find( size_t id ) const;

    /// stl-like iteration.
    inline iterator begin();
    inline iterator end();
    inline const_iterator begin() const;
    inline const_iterator end() const;

private:
    typedef StdSmMap< size_t, Error >::Map StdErrorMap;

    /// Ensure m_map is allocated, and return a reference.s
    inline StdErrorMap& ensureMap();

    /// Owned errors are stored in this map.
    /// \note Pointer so that we can get cheap copies
    ///   (return ErrorMap; is common usage).
    /// \note Allocated when first needed.  Keep empty ErrorMap cheap.
    shared_ptr< StdErrorMap > m_map;

    /// Empty map for iteration when m_map is unallocated.
    /// \note A class-static empty error map fails to link on wnti32
    /// when used in inline functions (error LNK2001; 2006/04/18).
    /// And we prefer the inlining over space, especially for end() (think loops).
    StdErrorMap m_emptyMap;
};

/**
    \brief Change from a bulk-reporting ErrorMap policy to an immediate-throw IFail exception policy.
    RAII class for temporary change and restore.
*/
class FCLASSES_API ExceptionPolicy
{
public:
    /// Change from a bulk-reporting to an immediate-throw policy
    /// for the lifetime of this object.
    /// \ref error_map_exception_policy
    ExceptionPolicy();
    /// Maybe change from a bulk-reporting to an immediate-throw policy
    /// for the lifetime of this object.
    /// Note that any active throw-on-error policy always wins!
    /// \param[in] throwIfail.
    ///    If true, set immediate-throw
    ///    If false, leave the policy unchanged.
    /// \ref error_map_exception_policy
    explicit ExceptionPolicy( bool throwOnError );
    /// Restore previous ErrorMap bulk-reporting policy (if different).
    ~ExceptionPolicy();

    /// Get current immendiate-throw policy.
    /// \ref error_map_exception_policy
    static bool throwOnError();

private:
    // Prohibit copy and assign.
    ExceptionPolicy( ExceptionPolicy const& );
    ExceptionPolicy& operator=( ExceptionPolicy const& );

    /// Need to change state of ErrorMap::s_throwOnError?
    bool m_setThrowOnError;

    /// Bulk-reporting policy - false by default.
    static bool s_throwOnError;
    static bool s_overrideThrowOnError;
    friend class IgnoreExceptionPolicy;
};

class FCLASSES_API IgnoreExceptionPolicy
{
public:
    IgnoreExceptionPolicy();
    ~IgnoreExceptionPolicy();
};

//// Implementation details

inline Error::Error()
: m_ifail( ITK_ok ), m_errorMessage()
{
}

inline Error::~Error()
{
    // Required to prevent warnings in std::vector-on-sm template compilation
    // with msvc7.1 compiler.
}

inline Error::Error( int ifail )
: m_ifail( ifail ), m_errorMessage()
{
    if ( ifail != ITK_ok )
    {
        m_errorMessage = Teamcenter::ErrorStoreBase::getInstance().ask_last_message( ifail ); // ifail must already been in ERROR_store
    }
}

inline bool Error::isOk() const
{
    return m_ifail == ITK_ok;
}

inline bool Error::isError() const
{
    return m_ifail != ITK_ok;
}

inline int Error::ifail() const
{
    return m_ifail;
}

inline std::string Error::errorMessage() const
{
    return m_errorMessage;
}

inline ErrorMap::ErrorMap()
: m_map(),
  m_emptyMap()
{
}

inline ErrorMap::ErrorMap( ErrorMap const& errorMap )
: m_map( errorMap.m_map ),
  m_emptyMap()
{
}

inline ErrorMap& ErrorMap::operator=( ErrorMap const& errorMap )
{
    // Deliberately not checking for self-assignment.
    // Exception-safe classes rarely need to whatever QAZ says.
    m_map = errorMap.m_map;
    // No need to copy m_emptyMap.  They are both empty!
    return *this;
}

inline ErrorMap& ErrorMap::add( size_t id, int ifail )
{
    return add( id, Teamcenter::Private::takeErrorStack( ifail ) );
}

inline ErrorMap& ErrorMap::add( size_t id, IFail const& ifail )
{
    return add( id, Teamcenter::Private::takeErrorStack( ifail ) );
}

inline ErrorMap& ErrorMap::add( size_t id, status_t const& status )
{
    return add( id, Teamcenter::Private::takeErrorStack( status ) );
}

inline size_t ErrorMap::size() const
{
    if ( m_map )
        return m_map->size();
    else
        return 0;
}
inline bool ErrorMap::empty() const
{
    if ( m_map )
        return m_map->empty();
    else
        return true;
}

inline ErrorMap::iterator ErrorMap::begin()
{
    if ( m_map )
        return m_map->begin();
    else
        return m_emptyMap.begin();
}

inline ErrorMap::iterator ErrorMap::end()
{
    if ( m_map )
        return m_map->end();
    else
        return m_emptyMap.end();
}

inline ErrorMap::const_iterator ErrorMap::begin() const
{
    if ( m_map )
        return m_map->begin();
    else
        return m_emptyMap.begin();
}

inline ErrorMap::const_iterator ErrorMap::end() const
{
    if ( m_map )
        return m_map->end();
    else
        return m_emptyMap.end();
}
}  // namespace Teamcenter
#include <fclasses/libfclasses_undef.h>
#endif
