// Copyright (c) 2010 Baidu, Inc.
// 
// Licensed 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.

// Author: Ge,Jun (gejun@baidu.com)
// Date: Sun Nov 7 21:43:34 CST 2010

#ifndef BUTIL_SYNCHRONOUS_EVENT_H
#define BUTIL_SYNCHRONOUS_EVENT_H

#include <vector>                             // std::vector
#include <algorithm>                          // std::find
#include <errno.h>                            // errno
#include "butil/logging.h"

// Synchronous event notification.
// Observers to an event will be called immediately in the same context where
// the event is notified. This utility uses a vector to store all observers
// thus is only suitable for a relatively small amount of observers.
//
// Example:
//    // Declare event type
//    typedef SynchronousEvent<int, const Foo*> FooEvent;
//
//    // Implement observer type
//    class FooObserver : public FooEvent::Observer {
//        void on_event(int, const Foo*) {
//            ... handle the event ...
//        }
//    };
//
//    FooEvent foo_event;                 // An instance of the event
//    FooObserver foo_observer;           // An instance of the observer
//    foo_event.subscribe(&foo_observer); // register the observer to the event
//    foo_event.notify(1, NULL);          // foo_observer.on_event(1, NULL) is
//                                        // called *immediately*

namespace butil {

namespace detail {
// NOTE: This is internal class. Inherit SynchronousEvent<..>::Observer instead.
template <typename _A1, typename _A2, typename _A3> class EventObserver;
}

// All methods are *NOT* thread-safe.
// You can copy a SynchronousEvent.
template <typename _A1 = void, typename _A2 = void, typename _A3 = void>
class SynchronousEvent {
public:
    typedef detail::EventObserver<_A1, _A2, _A3> Observer;
    typedef std::vector<Observer*> ObserverSet;
    
    SynchronousEvent() : _n(0) {}

    // Add an observer, callable inside on_event() and added observers
    // will be called with the same event in the same run.
    // Returns 0 when successful, -1 when the obsever is NULL or already added. 
    int subscribe(Observer* ob) {
        if (NULL == ob) {
            LOG(ERROR) << "Observer is NULL";
            return -1;
        }
        if (std::find(_obs.begin(), _obs.end(), ob) != _obs.end()) {
            return -1;
        }
        _obs.push_back(ob);
        ++_n;
        return 0;
    }

    // Remove an observer, callable inside on_event().
    // Users are responsible for removing observers before destroying them.
    // Returns 0 when successful, -1 when the observer is NULL or already removed.
    int unsubscribe(Observer* ob) {
        if (NULL == ob) {
            LOG(ERROR) << "Observer is NULL";
            return -1;
        }
        typename ObserverSet::iterator
            it = std::find(_obs.begin(), _obs.end(), ob);
        if (it == _obs.end()) {
            return -1;
        }
        *it = NULL;
        --_n;
        return 0;
    }

    // Remove all observers, callable inside on_event()
    void clear() {
        for (typename ObserverSet::iterator
                 it = _obs.begin(); it != _obs.end(); ++it) {
            *it = NULL;
        }
        _n = 0;
    }

    // Get number of observers
    size_t size() const { return _n; }

    // No observers or not
    bool empty() const { return size() == 0UL; }

    // Notify observers without parameter, errno will not be changed
    void notify() {
        const int saved_errno = errno;
        for (size_t i = 0; i < _obs.size(); ++i) {
            if (_obs[i]) {
                _obs[i]->on_event();
            }
        }
        _shrink();
        errno = saved_errno;
    }

    // Notify observers with 1 parameter, errno will not be changed
    template <typename _B1> void notify(const _B1& b1) {
        const int saved_errno = errno;
        for (size_t i = 0; i < _obs.size(); ++i) {
            if (_obs[i]) {
                _obs[i]->on_event(b1);
            }
        }
        _shrink();
        errno = saved_errno;
    }

    // Notify observers with 2 parameters, errno will not be changed
    template <typename _B1, typename _B2>
    void notify(const _B1& b1, const _B2& b2) {
        const int saved_errno = errno;
        for (size_t i = 0; i < _obs.size(); ++i) {
            if (_obs[i]) {
                _obs[i]->on_event(b1, b2);
            }
        }
        _shrink();
        errno = saved_errno;
    }

    // Notify observers with 3 parameters, errno will not be changed
    template <typename _B1, typename _B2, typename _B3>
    void notify(const _B1& b1, const _B2& b2, const _B3& b3) {
        const int saved_errno = errno;
        for (size_t i = 0; i < _obs.size(); ++i) {
            if (_obs[i]) {
                _obs[i]->on_event(b1, b2, b3);
            }
        }
        _shrink();
        errno = saved_errno;
    }
        
private:
    void _shrink() {
        if (_n == _obs.size()) {
            return;
        }
        for (typename ObserverSet::iterator
                 it1 = _obs.begin(),
                 it2 = _obs.begin(); it2 != _obs.end(); ++it2) {
            if (*it2) {
                *it1++ = *it2;
            }
        }
        _obs.resize(_n);
    }
    
    size_t _n;
    ObserverSet _obs;
};

namespace detail {

// Add const reference for types which is larger than sizeof(void*). This
// is reasonable in most cases and making signature of SynchronousEvent<...>
// cleaner.
template <typename T> struct _AddConstRef { typedef const T& type; };
template <typename T> struct _AddConstRef<T&> { typedef T& type; };

// We have to re-invent some wheels to avoid dependence on <boost/mpl/if.hpp>
template <bool cond, typename T1, typename T2>
struct if_c { typedef T1 type; };

template <typename T1, typename T2>
struct if_c<false, T1, T2> { typedef T2 type; };

template <typename T>
struct AddConstRef : public if_c<(sizeof(T)<=sizeof(void*)),
    T, typename _AddConstRef<T>::type> {};

template <> class EventObserver<void, void, void> {
public:
    virtual ~EventObserver() {}
    virtual void on_event() = 0;
};

template <typename _A1> class EventObserver<_A1, void, void> {
public:
    virtual ~EventObserver() {}
    virtual void on_event(typename AddConstRef<_A1>::type) = 0;
};

template <typename _A1, typename _A2> class EventObserver<_A1, _A2, void> {
public:
    virtual ~EventObserver() {}
    virtual void on_event(typename AddConstRef<_A1>::type,
                          typename AddConstRef<_A2>::type) = 0;
};

template <typename _A1, typename _A2, typename _A3> class EventObserver {
public:
    virtual ~EventObserver() {}
    virtual void on_event(typename AddConstRef<_A1>::type,
                          typename AddConstRef<_A2>::type,
                          typename AddConstRef<_A3>::type) = 0;
};
}  // end namespace detail
}  // end namespace butil

#endif  // BUTIL_SYNCHRONOUS_EVENT_H
