/*
 * Copyright Staffan Gimåker 2007-2010.
 *
 * ---
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef PEEKABOT_CLIENT_OPERATION_STATUS_HH_INCLUDED
#define PEEKABOT_CLIENT_OPERATION_STATUS_HH_INCLUDED


#include <string>
#include <boost/utility.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>


namespace peekabot
{
    namespace client
    {
        /**
         * \brief Enumerates all the different operation outcomes.
         */
        enum OperationOutcome
        {
            /**
             * \brief The operation outcome is still pending.
             */
            OUTCOME_PENDING = 0,

            /**
             * \brief The operation succeeded.
             */
            OPERATION_SUCCEEDED = 1,

            /**
             * \brief The operation failed miserably.
             */
            OPERATION_FAILED = 2
        };


        /**
         * \internal
         *
         * \brief Provides storage for and interaction with operation
         * statuses, for internal purposes. Provides a \emph thread-safe
         * interface for setting and getting a status, as well as inquiring
         * about its availability.
         *
         * The class facilitates storing operation statuses in a thread-safe,
         * friend-less and transparent manner by storing the actual status in
         * an object seperated from the public interface to it, \c Status.
         *
         * \sa \c Status, \c OperationResult
         */
        class OperationStatus : public boost::noncopyable
        {
        public:
            OperationStatus();


            virtual ~OperationStatus();


            /**
             * \brief Return the outcome of the associated operation.
             */
            OperationOutcome get_outcome() const;


            /**
             * \brief Set the outcome of associated operation.
             *
             * This should \e only be called by the peekabot client library
             * when a result is received from the server.
             *
             * \param outcome The outcome of the operation.
             * \param error_msg The the operation failed, this informational
             * error message should be supplied. It can be accessed to give the
             * user a more descriptive reason for the failure.
             */
            void set_outcome(
                OperationOutcome outcome,
                const std::string &error_msg = "");


            /**
             * \brief Get the error message associated with the outcome.
             *
             * \pre Defined only when
             * <tt>get_outcome() == OPERATION_FAILED</tt>, its value is
             * otherwise undefined.
             */
            std::string get_error_msg() const;


            /**
             * \brief Block until the outcome of the operation is available, or
             * the client is disconnected.
             *
             * \return Returns \c true if the client was disconnected before
             * a response was received, \c false otherwise.
             */
            bool wait_until_completed() const;


            /**
             * \brief Wake any threads waiting for the completion of the
             * operation status.
             */
            void wake() const;

            /**
             * \brief A method that must be called for each
             * OperationStatus-object when the client that the status request
             * originated from is disconnected.
             *
             * Calls wake() to cancel blocking calls on the object, e.g.
             * wait_until_completed(). This is essential as the client program
             * will otherwise hang for no apparent reason - waiting for the
             * outcome of an operation that'll never be gotten, since the link
             * between the server and client has been torn down!
             */
            void client_disconnected();

        private:
            /**
             * \brief Governs all access (read and write) to \emph all members.
             */
            mutable boost::mutex m_mutex;

            /**
             * \brief The outcome of the operation that the object is tracking.
             *
             * The initial value of this member is \e pending.
             */
            OperationOutcome m_outcome;

            /**
             * \brief The error message of the operation tracked by the object,
             * if applicable - it's defined only when the operation failed.
             */
            std::string m_error_msg;

            /**
             * \brief Condition that's used to wait for pending outcomes.
             *
             * It's waited on in \c wait_until_completed(), notifications are
             * generated by \c wake() and
             * <tt>set_outcome(OperationOutcome,const std::string &)</tt>.
             */
            mutable boost::condition m_outcome_cond;

            /**
             * \brief Set to true if the client that spawned the OperationStatus
             * was disconnected - if it was, all calls to wait_until_completed()
             * should return immediately.
             *
             * Initially set to \c false. Set to \c true in
             * client_disconnected().
             */
            volatile bool m_client_was_disconnected;
        };
    }
}

#endif // PEEKABOT_CLIENT_OPERATION_STATUS_HH_INCLUDED
