/*+==========================================================================
 *  Copyright (c) 2006 TeleNav, Inc
 *  All rights reserved
 *  This program is UNPUBLISHED PROPRIETARY property of TeleNav.
 *  Only for internal distribution.
 *
 *  Purpose: header file of class Error
 *  Author  jwchen2@telenav.cn
 *  Version 1.0, 2011/3/1
 *  Since   VIM 7.3
 *  ==========================================================================+*/

#ifndef Error_H_
#define Error_H_
#include <string>
#include "foundation/Logger.h"
namespace Tn
{
    namespace Foundation
    {
        template<class Module> struct ModuleInfo;

        template<class Module> class ErrorValueMetaType
        {
            public:
                explicit ErrorValueMetaType(int val, std::string txt="")
                    : m_val(val), m_msg(txt)
                {
                }
                bool operator==(const ErrorValueMetaType<Module>& rhs)
                {
                    return m_val == rhs.m_val;
                }
                int GetCode() const
                {
                    return m_val;
                }
                std::string GetMessage() const
                {
                    return m_msg;
                }
                void SetMessage(const std::string& msg)
                {
                    m_msg = msg;
                }
                std::string GetModuleName() const
                {
                    return ModuleInfo<Module>::name;
                }
            private:
                int m_val;
                std::string m_msg;
        };


        template < class Module > class ErrorDefinition;

        template<class Module> class NoCheckPolicy
        {
            typedef typename ErrorDefinition<Module>::ErrorType ErrorType;
            public:
                void OnErrorNoCheck(const ErrorType& v)
                {
                }
            protected:
                ~NoCheckPolicy()
                {
                }
        };

        template<class Module> class LogCheckPolicy
        {
            typedef typename ErrorDefinition<Module>::ErrorType ErrorType;
            public:
                LogCheckPolicy() : suppress_(true), logger_(NULL) {}
                void SetLogger(Logger* logger)
                {
                    logger_ = logger;
                }
                void SuppressNoError(bool b) 
                {
                    suppress_ = b;
                }
                void OnErrorNoCheck(ErrorType& v)
                {
                    if ( suppress_ && v == ErrorDefinition<Module>::ERROR_NO_ERROR ) return;
                    if ( logger_ != NULL ) {
                        std::string msg = "[WARNING:] No Check Error in Module:"
                            + v.GetModuleName() + " Message: "
                            + v.GetMessage();
                        logger_->Log(Logger_Priority_Warn, msg);
                    }
                }
            protected:
                ~LogCheckPolicy()
                {
                }
                bool suppress_;
                Logger* logger_;
        };

        /**
         * @brief Error class
         *
         * the user of error class must implements ErrorNumberDefinition class with specify enum value, for example
         *
         * in header file:
         * template<> class ErrorDefinition<NetworkService>{
         * public:
         *     typedef ErrorValueMetaType<NetworkService> ErrorType;
         *     static ErrorType ERROR_NO_ERROR;
         *     static ErrorType GENERIC_ERROR;
         *};
         *template<> struct ModuleInfo<NetworkService>
         *{
         *      static std::string name;
         *};
         *in cpp file
         * typedef ErrorDefinition<NetworkService> NetworkErrorDef;
         *
         * NetworkErrorDef::ErrorType NetworkErrorDef::ERROR_NO_ERROR = ErrorType(0, "NO Error");
         * NetworkErrorDef::ErrorType NetworkErrorDef::GENERIC_ERROR = ErrorType(1, "Generic Error");
         *
         * std::string ModuleInfo<NetworkService>::name = "NetworkService";
         *
         *
         * typedef Error<NetworkService, LogCheckPolicy> NetworkError;
         *
         * NetworkError err = ....
         *
         * then you need to check the err by
         * if ( err == NetworkError::ERROR_NO_ERROR ) ....
         * otherwise a WARNING message will be output, but this behavior
         * is configurable you can use
         * typedef Error<ET_NetworkService, NoCheckPolicy> NetworkError;
         * that means, the second template parameter is a extensibe point.
         *
         * @tparam Classification template parameter that used for marking
         * the category of error
         * @tparam CheckPolicy template parameter that used for check policy,
         * that means if user get one Error
         * but don't do anything on it, how do we handle this case,
         * Do nothing by default.
         */
        template <
            class Module,
            template <class> class CheckPolicy = LogCheckPolicy>
        class Error
            : public CheckPolicy<Module>,
            public ErrorDefinition<Module>
        {
            public:
                typedef typename ErrorDefinition<Module>::ErrorType ErrorType;
                explicit Error()
                    : m_val(ErrorDefinition<Module>::ERROR_NO_ERROR)
                      , m_bErrorChecked(false)
                      , m_bErrorSet(false)
                {
                }
                ~Error()
                {
                    checkErrorCheck();
                }
                void assign(const ErrorType& val)
                {
                    checkErrorCheck();
                    m_val = val;
                    m_bErrorChecked = false;
                    m_bErrorSet = true;
                }

                void clear()
                {
                    checkErrorCheck();
                    m_bErrorSet = false;
                    m_bErrorChecked = false;
                    m_val = ErrorDefinition<Module>::ERROR_NO_ERROR;
                }

                bool operator==(const ErrorType& err)
                {
                    m_bErrorChecked = true;
                    return m_val == err;
                }

                bool operator!=(const ErrorType& err)
                {
                    return !operator==(err);
                }

                operator bool()
                {
                    m_bErrorChecked = true;
                    return m_val == ErrorDefinition<Module>::ERROR_NO_ERROR;
                }
                bool operator!()  // true if no error
                {
                    m_bErrorChecked = true;
                    return m_val != ErrorDefinition<Module>::ERROR_NO_ERROR;
                }

                std::string GetErrorMessage() const
                {
                    return m_val.GetMessage();
                }

            private:
                void checkErrorCheck()
                {
                    if ( m_bErrorSet && !m_bErrorChecked )
                    {
                        CheckPolicy<Module>::OnErrorNoCheck(m_val);
                    }
                }

            private:
                ErrorType m_val;
                bool     m_bErrorChecked;
                bool    m_bErrorSet;
        };


        /**
         * @brief used to stand for a compile time error
         */

        template<bool> struct CompileTimeChecker
        {
            CompileTimeChecker(...);
        };
        template<> struct CompileTimeChecker<false> {};

        #define THROW_COMPILE_ERROR(msg) \
        { \
            class ERROR_##file##_##msg {}; \
            (void)CompileTimeChecker<false>(ERROR_##file##_##msg()); \
        }


        /**
         * @brief if the following function is invoked, you will get a compile time error
         * message will be like as follow:
         * ERROR__Compare_Type_Is_Not_Same
         */
        template<class Module, template<class> class CP, typename T>
        inline bool operator==(Error<Module, CP> v, T t)
        {
            THROW_COMPILE_ERROR(Compare_Type_Is_Not_Same);
            return false;
        }
        template<class Module, template<class> class CP, typename T>
        inline bool operator==(T t, Error<Module, CP> v)
        {
            THROW_COMPILE_ERROR(Compare_Type_Is_Not_Same);
            return false;
        }

        template<class Module, template<class> class CP, typename T>
        inline bool operator!=(Error<Module, CP> v, T t)
        {
            THROW_COMPILE_ERROR(Compare_Type_Is_Not_Same);
            return false;
        }
        template<class Module, template<class> class CP, typename T>
        inline bool operator!=(T t, Error<Module, CP> v)
        {
            THROW_COMPILE_ERROR(Compare_Type_Is_Not_Same);
            return false;
        }

    };  // end of namespace foundation
};  // end of namespace Tn

#endif
