text:
/* $Id: EventImpl.cpp $ */
/** @file
 * VirtualBox COM Event class implementation
 */

/*
 * Copyright (C) 2010-2014 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

/** @page pg_main_events    Events
 *
 * Theory of operations.
 *
 * This code implements easily extensible event mechanism, letting us
 * to make any VirtualBox object an event source (by aggregating an EventSource instance).
 * Another entity could subscribe to the event source for events it is interested in.
 * If an event is waitable, it's possible to wait until all listeners
 * registered at the moment of firing event as ones interested in this
 * event acknowledged that they finished event processing (thus allowing
 * vetoable events).
 *
 * Listeners can be registered as active or passive ones, defining policy of delivery.
 * For *active* listeners, their HandleEvent() method is invoked when event is fired by
 * the event source (pretty much callbacks).
 * For *passive* listeners, it's up to an event consumer to perform GetEvent() operation
 * with given listener, and then perform desired operation with returned event, if any.
 * For passive listeners case, listener instance serves as merely a key referring to
 * particular event consumer, thus HandleEvent() implementation isn't that important.
 * IEventSource's CreateListener() could be used to create such a listener.
 * Passive mode is designed for transports not allowing callbacks, such as webservices
 * running on top of HTTP, and for situations where consumer wants exact control on
 * context where event handler is executed (such as GUI thread for some toolkits).
 *
 * Internal EventSource data structures are optimized for fast event delivery, while
 * listener registration/unregistration operations are expected being pretty rare.
 * Passive mode listeners keep an internal event queue for all events they receive,
 * and all waitable events are added to the pending events map. This map keeps track
 * of how many listeners are still not acknowledged their event, and once this counter
 * reach zero, element is removed from pending events map, and event is marked as processed.
 * Thus if passive listener's user forgets to call IEventSource's EventProcessed()
 * waiters may never know that event processing finished.
 */

#include <list>
#include <map>
#include <deque>

#include "EventImpl.h"
#include "AutoCaller.h"
#include "Logging.h"

#include <iprt/semaphore.h>
#include <iprt/critsect.h>
#include <iprt/asm.h>
#include <iprt/time.h>

#include <VBox/com/array.h>

class ListenerRecord;

struct VBoxEvent::Data
{
    Data()
        : mType(VBoxEventType_Invalid),
          mWaitEvent(NIL_RTSEMEVENT),
          mWaitable(FALSE),
          mProcessed(FALSE)
    {}

    VBoxEventType_T         mType;
    RTSEMEVENT              mWaitEvent;
    BOOL                    mWaitable;
    BOOL                    mProcessed;
    ComPtr<IEventSource>    mSource;
};

DEFINE_EMPTY_CTOR_DTOR(VBoxEvent)

HRESULT VBoxEvent::FinalConstruct()
{
    m = new Data;
    return BaseFinalConstruct();
}

void VBoxEvent::FinalRelease()
{
    if (m)
    {
        uninit();
        delete m;
        m = NULL;
    }
    BaseFinalRelease();
}

HRESULT VBoxEvent::init(IEventSource *aSource, VBoxEventType_T aType, BOOL aWaitable)
{
    HRESULT rc = S_OK;

    AssertReturn(aSource != NULL, E_INVALIDARG);

    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    m->mSource = aSource;
    m->mType = aType;
    m->mWaitable = aWaitable;
    m->mProcessed = !aWaitable;

    do {
        if (aWaitable)
        {
            int vrc = ::RTSemEventCreate(&m->mWaitEvent);

            if (RT_FAILURE(vrc))
            {
                AssertFailed();
                return setError(E_FAIL,
                                tr("Internal error (%Rrc)"), vrc);
            }
        }
    } while (0);

    /* Confirm a successful initialization */
    autoInitSpan.setSucceeded();

    return rc;
}

void VBoxEvent::uninit()
{
    AutoUninitSpan autoUninitSpan(this);
    if (autoUninitSpan.uninitDone())
        return;

    if (!m)
        return;

    m->mProcessed = TRUE;
    m->mType = VBoxEventType_Invalid;
    m->mSource.setNull();

    if (m->mWaitEvent != NIL_RTSEMEVENT)
    {
        Assert(m->mWaitable);
        ::RTSemEventDestroy(m->mWaitEvent);
        m->mWaitEvent = NIL_RTSEMEVENT;
    }
}

HRESULT VBoxEvent::getType(VBoxEventType_T *aType)
{
    // never changes while event alive, no locking
    *aType = m->mType;
    return S_OK;
}

HRESULT VBoxEvent::getSource(ComPtr<IEventSource> &aSource)
{
    m->mSource.queryInterfaceTo(aSource.asOutParam());
    return S_OK;
}

HRESULT VBoxEvent::getWaitable(BOOL *aWaitable)
{
    // never changes while event alive, no locking
    *aWaitable = m->mWaitable;
    return S_OK;
}

HRESULT VBoxEvent::setProcessed()
{
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);

    if (m->mProcessed)
        return S_OK;

    m->mProcessed = TRUE;

    // notify waiters
    ::RTSemEventSignal(m->mWaitEvent);

    return S_OK;
}

HRESULT VBoxEvent::waitProcessed(LONG aTimeout, BOOL *aResult)
{
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);

    if (m->mProcessed)
    {
        *aResult = TRUE;
        return S_OK;
    }

    if (aTimeout == 0)
    {
        *aResult = m->mProcessed;
        return S_OK;
    }

    /* @todo: maybe while loop for spurious wakeups? */
    int vrc = ::RTSemEventWait(m->mWaitEvent, aTimeout);
    AssertMsg(RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
              ("RTSemEventWait returned %Rrc\n", vrc));

    if (RT_SUCCESS(vrc))
    {
        AssertMsg(m->mProcessed,
                  ("mProcessed must be set here\n"));
        *aResult = m->mProcessed;
    }
    else
    {
        *aResult = FALSE;
    }

    return S_OK;
}

typedef std::list<Utf8Str> VetoList;
typedef std::list<Utf8Str> ApprovalList;
struct VBoxVetoEvent::Data
{
    Data() :
        mVetoed(FALSE)
    {}
    ComObjPtr<VBoxEvent>    mEvent;
    BOOL                    mVetoed;
    VetoList                mVetoList;
    ApprovalList            mApprovalList;
};

HRESULT VBoxVetoEvent::FinalConstruct()
{
    m = new Data;
    HRESULT rc = m->mEvent.createObject();
    BaseFinalConstruct();
    return rc;
}

void VBoxVetoEvent::FinalRelease()
{
    if (m)
    {
        uninit();
        delete m;
        m = NULL;
    }
    BaseFinalRelease();
}

DEFINE_EMPTY_CTOR_DTOR(VBoxVetoEvent)

HRESULT VBoxVetoEvent::init(IEventSource *aSource, VBoxEventType_T aType)
{
    HRESULT rc = S_OK;
    // all veto events are waitable
    rc = m->mEvent->init(aSource, aType, TRUE);
    if (FAILED(rc))
        return rc;

    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    m->mVetoed = FALSE;
    m->mVetoList.clear();
    m->mApprovalList.clear();

    /* Confirm a successful initialization */
    autoInitSpan.setSucceeded();

    return S_OK;
}

void VBoxVetoEvent::uninit()
{
    AutoUninitSpan autoUninitSpan(this);
    if (autoUninitSpan.uninitDone())
        return;

    if (!m)
        return;

    m->mVetoed = FALSE;
    if (!m->mEvent.isNull())
    {
        m->mEvent->uninit();
        m->mEvent.setNull();
    }
}

HRESULT VBoxVetoEvent::getType(VBoxEventType_T *aType)
{
    return m->mEvent->COMGETTER(Type)(aType);
}

HRESULT VBoxVetoEvent::getSource(ComPtr<IEventSource> &aSource)
{
    return m->mEvent->COMGETTER(Source)(aSource.asOutParam());
}

HRESULT VBoxVetoEvent::getWaitable(BOOL *aWaitable)
{
    return m->mEvent->COMGETTER(Waitable)(aWaitable);
}

HRESULT VBoxVetoEvent::setProcessed()
{
    return m->mEvent->SetProcessed();
}

HRESULT VBoxVetoEvent::waitProcessed(LONG aTimeout, BOOL *aResult)
{
    return m->mEvent->WaitProcessed(aTimeout, aResult);
}

HRESULT VBoxVetoEvent::addVeto(const com::Utf8Str &aReason)
{
    // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    if (aReason.length())
        m->mVetoList.push_back(aReason);

    m->mVetoed = TRUE;

    return S_OK;
}

HRESULT VBoxVetoEvent::isVetoed(BOOL *aResult)
{
    // AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    *aResult = m->mVetoed;

    return S_OK;
}

HRESULT VBoxVetoEvent::getVetos(std::vector<com::Utf8Str> &aResult)
{
    // AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    aResult.resize(m->mVetoList.size());
    size_t i = 0;
    for (VetoList::const_iterator it = m->mVetoList.begin(); it != m->mVetoList.end(); ++it, ++i)
        aResult[i] = (*it);

    return S_OK;

}

HRESULT VBoxVetoEvent::addApproval(const com::Utf8Str &aReason)
{
    // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    m->mApprovalList.push_back(aReason);
    return S_OK;
}

HRESULT VBoxVetoEvent::isApproved(BOOL *aResult)
{
    // AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    *aResult = !m->mApprovalList.empty();
    return S_OK;
}

HRESULT VBoxVetoEvent::getApprovals(std::vector<com::Utf8Str> &aResult)
{
    // AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    aResult.resize(m->mApprovalList.size());
    size_t i = 0;
    for (ApprovalList::const_iterator it = m->mApprovalList.begin(); it != m->mApprovalList.end(); ++it, ++i)
        aResult[i] = (*it);
    return S_OK;
}

static const int FirstEvent = (int)VBoxEventType_LastWildcard + 1;
static const int LastEvent = (int)VBoxEventType_Last;
static const int NumEvents = LastEvent - FirstEvent;

/**
 * Class replacing std::list and able to provide required stability
 * during iteration. It's acheived by delaying structural modifications
 * to the list till the moment particular element is no longer used by
 * current iterators.
 */
class EventMapRecord
{
public:
    /**
     * We have to be double linked, as structural modifications in list are delayed
     * till element removed, so we have to know our previous one to update its next
     */
    EventMapRecord *mNext;
    bool            mAlive;
private:
    EventMapRecord *mPrev;
    ListenerRecord *mRef; /* must be weak reference */
    int32_t         mRefCnt;

public:
    EventMapRecord(ListenerRecord *aRef) :
        mNext(0), mAlive(true), mPrev(0), mRef(aRef), mRefCnt(1)
    {}

    EventMapRecord(EventMapRecord &aOther)
    {
        mNext = aOther.mNext;
        mPrev = aOther.mPrev;
        mRef = aOther.mRef;
        mRefCnt = aOther.mRefCnt;
        mAlive = aOther.mAlive;
    }

    ~EventMapRecord()
    {
        if (mNext)
            mNext->mPrev = mPrev;
        if (mPrev)
            mPrev->mNext = mNext;
    }

    void addRef()
    {
        ASMAtomicIncS32(&mRefCnt);
    }

    void release()
    {
        if (ASMAtomicDecS32(&mRefCnt) <= 0)
            delete this;
    }

    // Called when an element is no longer needed
    void kill()
    {
        mAlive = false;
        release();
    }

    ListenerRecord *ref()
    {
        return mAlive ? mRef : 0;
    }

    friend class EventMapList;
};

class EventMapList
{
    EventMapRecord *mHead;
    uint32_t        mSize;
public:
    EventMapList()
        :
        mHead(0),
        mSize(0)
    {}
    ~EventMapList()
    {
        EventMapRecord *pCur = mHead;
        while (pCur)
        {
            EventMapRecord *pNext = pCur->mNext;
            pCur->release();
            pCur = pNext;
        }
    }

    /*
     * Elements have to be added to the front of the list, to make sure
     * that iterators doesn't see newly added listeners, and iteration
     * will always complete.
     */
    void add(ListenerRecord *aRec)
    {
        EventMapRecord *pNew = new EventMapRecord(aRec);
        pNew->mNext = mHead;
        if (mHead)
            mHead->mPrev = pNew;
        mHead = pNew;
        mSize++;
    }

    /*
     * Mark element as removed, actual removal could be delayed until
     * all consumers release it too. This helps to keep list stable
     * enough for iterators to allow long and probably intrusive callbacks.
     */
    void remove(ListenerRecord *aRec)
    {
        EventMapRecord *pCur = mHead;
        while (pCur)
        {
            EventMapRecord *aNext = pCur->mNext;
            if (pCur->ref() == aRec)
            {
                if (pCur == mHead)
                    mHead = aNext;
                pCur->kill();
                mSize--;
                // break?
            }
            pCur = aNext;
        }
    }

    uint32_t size() const
    {
        return mSize;
    }

    struct iterator
    {
        EventMapRecord *mCur;

        iterator() :
            mCur(0)
        {}

        explicit
        iterator(EventMapRecord *aCur) :
            mCur(aCur)
        {
            // Prevent element removal, till we're at it
            if (mCur)
                mCur->addRef();
        }

        ~iterator()
        {
            if (mCur)
                mCur->release();
        }

        ListenerRecord *
        operator*() const
        {
            return mCur->ref();
        }

        EventMapList::iterator &
        operator++()
        {
            EventMapRecord *pPrev = mCur;
            do {
                mCur = mCur->mNext;
            } while (mCur && !mCur->mAlive);

            // now we can safely release previous element
            pPrev->release();

            // And grab the new current
            if (mCur)
                mCur->addRef();

            return *this;
        }

        bool
        operator==(const EventMapList::iterator &aOther) const
        {
            return mCur == aOther.mCur;
        }

        bool
        operator!=(const EventMapList::iterator &aOther) const
        {
            return mCur != aOther.mCur;
        }
    };

    iterator begin()
    {
        return iterator(mHead);
    }

    iterator end()
    {
        return iterator(0);
    }
};

typedef EventMapList EventMap[NumEvents];
typedef std::map<IEvent *, int32_t> PendingEventsMap;
typedef std::deque<ComPtr<IEvent> > PassiveQueue;

class ListenerRecord
{
private:
    ComPtr<IEventListener>        mListener;
    BOOL                          mActive;
    EventSource                  *mOwner;

    RTSEMEVENT                    mQEvent;
    int32_t volatile              mWaitCnt;
    RTCRITSECT                    mcsQLock;
    PassiveQueue                  mQueue;
    int32_t volatile              mRefCnt;
    uint64_t                      mLastRead;

public:
    ListenerRecord(IEventListener *aListener,
                   com::SafeArray<VBoxEventType_T> &aInterested,
                   BOOL aActive,
                   EventSource *aOwner);
    ~ListenerRecord();

    HRESULT process(IEvent *aEvent, BOOL aWaitable, PendingEventsMap::iterator &pit, AutoLockBase &alock);
    HRESULT enqueue(IEvent *aEvent);
    HRESULT dequeue(IEvent **aEvent, LONG aTimeout, AutoLockBase &aAlock);
    HRESULT eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit);
    void shutdown();

    void addRef()
    {
        ASMAtomicIncS32(&mRefCnt);
    }

    void release()
    {
        if (ASMAtomicDecS32(&mRefCnt) <= 0)
            delete this;
    }

    BOOL isActive()
    {
        return mActive;
    }

    friend class EventSource;
};

/* Handy class with semantics close to ComPtr, but for list records */
template<typename Held>
class RecordHolder
{
public:
    RecordHolder(Held *lr) :
        held(lr)
    {
        addref();
    }
    RecordHolder(const RecordHolder &that) :
        held(that.held)
    {
        addref();
    }
    RecordHolder()
    :
    held(0)
    {
    }
    ~RecordHolder()
    {
        release();
    }

    Held *obj()
    {
        return held;
    }

    RecordHolder &operator=(const RecordHolder &that)
    {
        safe_assign(that.held);
        return *this;
    }
private:
    Held *held;

    void addref()
    {
        if (held)
            held->addRef();
    }
    void release()
    {
        if (held)
            held->release();
    }
    void safe_assign(Held *that_p)
    {
        if (that_p)
            that_p->addRef();
        release();
        held = that_p;
    }
};

typedef std::map<IEventListener *, RecordHolder<ListenerRecord> > Listeners;

struct EventSource::Data
{
    Data() : fShutdown(false)
    {}

    Listeners                     mListeners;
    EventMap                      mEvMap;
    PendingEventsMap              mPendingMap;
    bool                          fShutdown;
};

/**
 * This function defines what wildcard expands to.
 */
static BOOL implies(VBoxEventType_T who, VBoxEventType_T what)
{
    switch (who)
    {
        case VBoxEventType_Any:
            return TRUE;
        case VBoxEventType_Vetoable:
            return    (what == VBoxEventType_OnExtraDataCanChange)
                   || (what == VBoxEventType_OnCanShowWindow);
        case VBoxEventType_MachineEvent:
            return    (what == VBoxEventType_OnMachineStateChanged)
                   || (what == VBoxEventType_OnMachineDataChanged)
                   || (what == VBoxEventType_OnMachineRegistered)
                   || (what == VBoxEventType_OnSessionStateChanged)
                   || (what == VBoxEventType_OnGuestPropertyChanged);
        case VBoxEventType_SnapshotEvent:
            return    (what == VBoxEventType_OnSnapshotTaken)
                   || (what == VBoxEventType_OnSnapshotDeleted)
                   || (what == VBoxEventType_OnSnapshotChanged) ;
        case VBoxEventType_InputEvent:
            return    (what == VBoxEventType_OnKeyboardLedsChanged)
                   || (what == VBoxEventType_OnMousePointerShapeChanged)
                   || (what == VBoxEventType_OnMouseCapabilityChanged);
        case VBoxEventType_Invalid:
            return FALSE;
        default:
            break;
    }

    return who == what;
}

ListenerRecord::ListenerRecord(IEventListener *aListener,
                               com::SafeArray<VBoxEventType_T> &aInterested,
                               BOOL aActive,
                               EventSource *aOwner) :
    mActive(aActive), mOwner(aOwner), mWaitCnt(0), mRefCnt(0)
{
    mListener = aListener;
    EventMap *aEvMap = &aOwner->m->mEvMap;

    for (size_t i = 0; i < aInterested.size(); ++i)
    {
        VBoxEventType_T interested = aInterested[i];
        for (int j = FirstEvent; j < LastEvent; j++)
        {
            VBoxEventType_T candidate = (VBoxEventType_T)j;
            if (implies(interested, candidate))
            {
                (*aEvMap)[j - FirstEvent].add(this);
            }
        }
    }

    if (!mActive)
    {
        ::RTCritSectInit(&mcsQLock);
        ::RTSemEventCreate(&mQEvent);
        mLastRead = RTTimeMilliTS();
    }
    else
    {
        mQEvent = NIL_RTSEMEVENT;
        RT_ZERO(mcsQLock);
        mLastRead = 0;
    }
}

ListenerRecord::~ListenerRecord()
{
    /* Remove references to us from the event map */
    EventMap *aEvMap = &mOwner->m->mEvMap;
    for (int j = FirstEvent; j < LastEvent; j++)
    {
        (*aEvMap)[j - FirstEvent].remove(this);
    }

    if (!mActive)
    {
        // at this moment nobody could add elements to our queue, so we can safely
        // clean it up, otherwise there will be pending events map elements
        PendingEventsMap *aPem = &mOwner->m->mPendingMap;
        while (true)
        {
            ComPtr<IEvent> aEvent;

            if (mQueue.empty())
                break;

            mQueue.front().queryInterfaceTo(aEvent.asOutParam());
            mQueue.pop_front();

            BOOL aWaitable = FALSE;
            aEvent->COMGETTER(Waitable)(&aWaitable);
            if (aWaitable)
            {
                PendingEventsMap::iterator pit = aPem->find(aEvent);
                if (pit != aPem->end())
                    eventProcessed(aEvent, pit);
            }
        }

        ::RTCritSectDelete(&mcsQLock);
    }
    shutdown();
}

HRESULT ListenerRecord::process(IEvent *aEvent,
                                BOOL aWaitable,
                                PendingEventsMap::iterator &pit,
                                AutoLockBase &aAlock)
{
    if (mActive)
    {
        /*
         * We release lock here to allow modifying ops on EventSource inside callback.
         */
        HRESULT rc = S_OK;
        if (mListener)
        {
            aAlock.release();
            rc = mListener->HandleEvent(aEvent);
#ifdef RT_OS_WINDOWS
            Assert(rc != RPC_E_WRONG_THREAD);
#endif
            aAlock.acquire();
        }
        if (aWaitable)
            eventProcessed(aEvent, pit);
        return rc;
    }
    return enqueue(aEvent);
}

HRESULT ListenerRecord::enqueue(IEvent *aEvent)
{
    AssertMsg(!mActive, ("must be passive\n"));

    // put an event the queue
    ::RTCritSectEnter(&mcsQLock);

    // If there was no events reading from the listener for the long time,
    // and events keep coming, or queue is oversized we shall unregister this listener.
    uint64_t sinceRead = RTTimeMilliTS() - mLastRead;
    size_t queueSize = mQueue.size();
    if ((queueSize > 1000) || ((queueSize > 500) && (sinceRead > 60 * 1000)))
    {
        ::RTCritSectLeave(&mcsQLock);
        return E_ABORT;
    }

    if (queueSize != 0 && mQueue.back() == aEvent)
        /* if same event is being pushed multiple times - it's reusable event and
           we don't really need multiple instances of it in the queue */
        (void)aEvent;
    else
        mQueue.push_back(aEvent);

    ::RTCritSectLeave(&mcsQLock);

    // notify waiters
    ::RTSemEventSignal(mQEvent);

    return S_OK;
}

HRESULT ListenerRecord::dequeue(IEvent **aEvent,
                                LONG aTimeout,
                                AutoLockBase &aAlock)
{
    if (mActive)
        return VBOX_E_INVALID_OBJECT_STATE;

    // retain listener record
    RecordHolder<ListenerRecord> holder(this);

    ::RTCritSectEnter(&mcsQLock);

    mLastRead = RTTimeMilliTS();

    if (mQueue.empty())
    {
        ::RTCritSectLeave(&mcsQLock);
        // Speed up common case
        if (aTimeout == 0)
        {
            *aEvent = NULL;
            return S_OK;
        }
        // release lock while waiting, listener will not go away due to above holder
        aAlock.release();

        // In order to safely shutdown, count all waiting threads here.
        ASMAtomicIncS32(&mWaitCnt);
        ::RTSemEventWait(mQEvent, aTimeout);
        ASMAtomicDecS32(&mWaitCnt);

        // reacquire lock
        aAlock.acquire();
        ::RTCritSectEnter(&mcsQLock);
    }
    if (mQueue.empty())
    {
        *aEvent = NULL;
    }
    else
    {
        mQueue.front().queryInterfaceTo(aEvent);
        mQueue.pop_front();
    }
    ::RTCritSectLeave(&mcsQLock);
    return S_OK;
}

HRESULT ListenerRecord::eventProcessed(IEvent *aEvent, PendingEventsMap::iterator &pit)
{
    if (--pit->second == 0)
    {
        Assert(pit->first == aEvent);
        aEvent->SetProcessed();
        mOwner->m->mPendingMap.erase(pit);
    }

    return S_OK;
}

void ListenerRecord::shutdown()
{
    if (mQEvent != NIL_RTSEMEVENT)
    {
        RTSEMEVENT tmp = mQEvent;
        mQEvent = NIL_RTSEMEVENT;

        /* On Darwin it is known that RTSemEventDestroy() returns 0 while
         * corresponding thread remains to be blocked after that. In order to prevent
         * undesireble freeze on shutdown, this workaround is used. */
        Log(("Wait for %d waiters to release.\n", ASMAtomicReadS32(&mWaitCnt)));
        while (ASMAtomicReadS32(&mWaitCnt) > 0)
        {
            ::RTSemEventSignal(tmp);

            /* Are we already done? */
            if (ASMAtomicReadS32(&mWaitCnt) == 0)
                break;

            RTThreadSleep(10);
        }
        Log(("All waiters just released the lock.\n"));

        ::RTSemEventDestroy(tmp);
    }
}

EventSource::EventSource()
{}

EventSource::~EventSource()
{}

HRESULT EventSource::FinalConstruct()
{
    m = new Data;
    return BaseFinalConstruct();
}

void EventSource::FinalRelease()
{
    uninit();
    delete m;
    BaseFinalRelease();
}

HRESULT EventSource::init()
{
    HRESULT rc = S_OK;

    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    /* Confirm a successful initialization */
    autoInitSpan.setSucceeded();
    return rc;
}

void EventSource::uninit()
{
    {
        // First of all (before even thinking about entering the uninit span):
        // make sure that all listeners are are shut down (no pending events or
        // wait calls), because they cannot be alive without the associated
        // event source. Otherwise API clients which use long-term (or
        // indefinite) waits will block VBoxSVC termination (just one example)
        // for a long time or even infinitely long.
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
        if (!m->fShutdown)
        {
            m->fShutdown = true;
            for (Listeners::iterator it = m->mListeners.begin();
                 it != m->mListeners.end();
                 ++it)
            {
                it->second.obj()->shutdown();
            }
        }
    }

    AutoUninitSpan autoUninitSpan(this);
    if (autoUninitSpan.uninitDone())
        return;

    m->mListeners.clear();
    // m->mEvMap shall be cleared at this point too by destructors, assert?
}

HRESULT EventSource::registerListener(const ComPtr<IEventListener> &aListener,
                                      const std::vector<VBoxEventType_T> &aInteresting,
                                      BOOL aActive)
{
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);

    if (m->fShutdown)
        return setError(VBOX_E_INVALID_OBJECT_STATE,
                        tr("This event source is already shut down"));

    Listeners::const_iterator it = m->mListeners.find(aListener);
    if (it != m->mListeners.end())
        return setError(E_INVALIDARG,
                        tr("This listener already registered"));

    com::SafeArray<VBoxEventType_T> interested(aInteresting);
    RecordHolder<ListenerRecord> lrh(new ListenerRecord(aListener, interested, aActive, this));
    m->mListeners.insert(Listeners::value_type((IEventListener *)aListener, lrh));

    VBoxEventDesc evDesc;
    evDesc.init(this, VBoxEventType_OnEventSourceChanged, (IEventListener *)aListener, TRUE);
    evDesc.fire(0);

    return S_OK;
}

HRESULT EventSource::unregisterListener(const ComPtr<IEventListener> &aListener)
{
    HRESULT rc = S_OK;;

    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);

    Listeners::iterator it = m->mListeners.find(aListener);

    if (it != m->mListeners.end())
    {
        it->second.obj()->shutdown();
        m->mListeners.erase(it);
        // destructor removes refs from the event map
        rc = S_OK;
    }
    else
    {
        rc = setError(VBOX_E_OBJECT_NOT_FOUND,
                      tr("Listener was never registered"));
    }

    if (SUCCEEDED(rc))
    {
        VBoxEventDesc evDesc;
        evDesc.init(this, VBoxEventType_OnEventSourceChanged, (IEventListener *)aListener, FALSE);
        evDesc.fire(0);
    }

    return rc;
}

HRESULT EventSource::fireEvent(const ComPtr<IEvent> &aEvent,
                               LONG aTimeout,
                               BOOL *aResult)
{

    HRESULT hrc = S_OK;
    BOOL aWaitable = FALSE;
    aEvent->COMGETTER(Waitable)(&aWaitable);

    do {
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);

        if (m->fShutdown)
            return setError(VBOX_E_INVALID_OBJECT_STATE,
                            tr("This event source is already shut down"));

        VBoxEventType_T evType;
        hrc = aEvent->COMGETTER(Type)(&evType);
        AssertComRCReturn(hrc, hrc);

        EventMapList &listeners = m->mEvMap[(int)evType - FirstEvent];

        /* Anyone interested in this event? */
        uint32_t cListeners = listeners.size();
        if (cListeners == 0)
        {
            aEvent->SetProcessed();
            break; // just leave the lock and update event object state
        }

        PendingEventsMap::iterator pit;

        if (aWaitable)
        {
            m->mPendingMap.insert(PendingEventsMap::value_type(aEvent, cListeners));
            // we keep iterator here to allow processing active listeners without
            // pending events lookup
            pit = m->mPendingMap.find(aEvent);
        }
        for (EventMapList::iterator it = listeners.begin();
             it != listeners.end();
             ++it)
        {
            HRESULT cbRc;
            // keep listener record reference, in case someone will remove it while in callback
            RecordHolder<ListenerRecord> record(*it);

            /*
             * We pass lock here to allow modifying ops on EventSource inside callback
             * in active mode. Note that we expect list iterator stability as 'alock'
             * could be temporary released when calling event handler.
             */
            cbRc = record.obj()->process(aEvent, aWaitable, pit, alock);

            /* Note that E_ABORT is used above to signal that a passive
             * listener was unregistered due to not picking up its event.
             * This overlaps with XPCOM specific use of E_ABORT to signal
             * death of an active listener, but that's irrelevant here. */
            if (FAILED_DEAD_INTERFACE(cbRc) || cbRc == E_ABORT)
            {
                Listeners::iterator lit = m->mListeners.find(record.obj()->mListener);
                if (lit != m->mListeners.end())
                {
                    lit->second.obj()->shutdown();
                    m->mListeners.erase(lit);
                }
            }
            // anything else to do with cbRc?
        }
    } while (0);
    /* We leave the lock here */

    if (aWaitable)
        hrc = aEvent->WaitProcessed(aTimeout, aResult);
    else
        *aResult = TRUE;

    return hrc;
}

HRESULT EventSource::getEvent(const ComPtr<IEventListener> &aListener,
                              LONG aTimeout,
                              ComPtr<IEvent> &aEvent)
{
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);

    if (m->fShutdown)
        return setError(VBOX_E_INVALID_OBJECT_STATE,
                        tr("This event source is already shut down"));

    Listeners::iterator it = m->mListeners.find(aListener);
    HRESULT rc = S_OK;

    if (it != m->mListeners.end())
        rc = it->second.obj()->dequeue(aEvent.asOutParam(), aTimeout, alock);
    else
        rc = setError(VBOX_E_OBJECT_NOT_FOUND,
                      tr("Listener was never registered"));

    if (rc == VBOX_E_INVALID_OBJECT_STATE)
        return setError(rc, tr("Listener must be passive"));

    return rc;
}

HRESULT EventSource::eventProcessed(const ComPtr<IEventListener> &aListener,
                                    const ComPtr<IEvent> &aEvent)
{
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);

    if (m->fShutdown)
        return setError(VBOX_E_INVALID_OBJECT_STATE,
                        tr("This event source is already shut down"));

    Listeners::iterator it = m->mListeners.find(aListener);
    HRESULT rc;

    BOOL aWaitable = FALSE;
    aEvent->COMGETTER(Waitable)(&aWaitable);

    if (it != m->mListeners.end())
    {
        ListenerRecord *aRecord = it->second.obj();

        if (aRecord->isActive())
            return setError(E_INVALIDARG,
                            tr("Only applicable to passive listeners"));

        if (aWaitable)
        {
            PendingEventsMap::iterator pit = m->mPendingMap.find(aEvent);

            if (pit == m->mPendingMap.end())
            {
                AssertFailed();
                rc = setError(VBOX_E_OBJECT_NOT_FOUND,
                              tr("Unknown event"));
            }
            else
                rc = aRecord->eventProcessed(aEvent, pit);
        }
        else
        {
            // for non-waitable events we're done
            rc = S_OK;
        }
    }
    else
    {
        rc = setError(VBOX_E_OBJECT_NOT_FOUND,
                      tr("Listener was never registered"));
    }

    return rc;
}

/**
 * This class serves as feasible listener implementation
 * which could be used by clients not able to create local
 * COM objects, but still willing to receive event
 * notifications in passive mode, such as webservices.
 */
class ATL_NO_VTABLE PassiveEventListener :
    public VirtualBoxBase,
    VBOX_SCRIPTABLE_IMPL(IEventListener)
{
public:

    VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(PassiveEventListener, IEventListener)

    DECLARE_NOT_AGGREGATABLE(PassiveEventListener)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(PassiveEventListener)
        VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
    END_COM_MAP()

    PassiveEventListener()
    {}
    ~PassiveEventListener()
    {}

    HRESULT FinalConstruct()
    {
        return BaseFinalConstruct();
    }
    void FinalRelease()
    {
        BaseFinalRelease();
    }

    // IEventListener methods
    STDMETHOD(HandleEvent)(IEvent *)
    {
        ComAssertMsgRet(false, ("HandleEvent() of wrapper shall never be called"),
                        E_FAIL);
    }
};

/* Proxy listener class, used to aggregate multiple event sources into one */
class ATL_NO_VTABLE ProxyEventListener :
    public VirtualBoxBase,
    VBOX_SCRIPTABLE_IMPL(IEventListener)
{
    ComPtr<IEventSource> mSource;
public:

    VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(ProxyEventListener, IEventListener)

    DECLARE_NOT_AGGREGATABLE(ProxyEventListener)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(ProxyEventListener)
        VBOX_DEFAULT_INTERFACE_ENTRIES(IEventListener)
    END_COM_MAP()

    ProxyEventListener()
    {}
    ~ProxyEventListener()
    {}

    HRESULT FinalConstruct()
    {
        return BaseFinalConstruct();
    }
    void FinalRelease()
    {
        BaseFinalRelease();
    }

    HRESULT init(IEventSource *aSource)
    {
        mSource = aSource;
        return S_OK;
    }

    // IEventListener methods
    STDMETHOD(HandleEvent)(IEvent *aEvent)
    {
        BOOL fProcessed = FALSE;
        if (mSource)
            return mSource->FireEvent(aEvent, 0, &fProcessed);
        else
            return S_OK;
    }
};

class ATL_NO_VTABLE EventSourceAggregator :
    public VirtualBoxBase,
    VBOX_SCRIPTABLE_IMPL(IEventSource)
{
    typedef std::list <ComPtr<IEventSource> > EventSourceList;
    /* key is weak reference */
    typedef std::map<IEventListener *, ComPtr<IEventListener> > ProxyListenerMap;

    EventSourceList           mEventSources;
    ProxyListenerMap          mListenerProxies;
    ComObjPtr<EventSource>    mSource;

public:

    VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(EventSourceAggregator, IEventSource)

    DECLARE_NOT_AGGREGATABLE(EventSourceAggregator)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(EventSourceAggregator)
        VBOX_DEFAULT_INTERFACE_ENTRIES(IEventSource)
    END_COM_MAP()

    EventSourceAggregator()
    {}
    ~EventSourceAggregator()
    {}

    HRESULT FinalConstruct()
    {
        return BaseFinalConstruct();
    }
    void FinalRelease()
    {
        mEventSources.clear();
        mListenerProxies.clear();
        mSource->uninit();
        BaseFinalRelease();
    }

    // internal public
    HRESULT init(const std::vector<ComPtr<IEventSource> >  aSourcesIn);

    // IEventSource methods
    STDMETHOD(CreateListener)(IEventListener **aListener);
    STDMETHOD(CreateAggregator)(ComSafeArrayIn(IEventSource *, aSubordinates),
                                IEventSource **aAggregator);
    STDMETHOD(RegisterListener)(IEventListener *aListener,
                                ComSafeArrayIn(VBoxEventType_T, aInterested),
                                BOOL aActive);
    STDMETHOD(UnregisterListener)(IEventListener *aListener);
    STDMETHOD(FireEvent)(IEvent *aEvent,
                         LONG aTimeout,
                         BOOL *aProcessed);
    STDMETHOD(GetEvent)(IEventListener *aListener,
                        LONG aTimeout,
                        IEvent **aEvent);
    STDMETHOD(EventProcessed)(IEventListener *aListener,
                              IEvent *aEvent);

  protected:
    HRESULT createProxyListener(IEventListener *aListener,
                                IEventListener **aProxy);
    HRESULT getProxyListener(IEventListener *aListener,
                             IEventListener **aProxy);
    HRESULT removeProxyListener(IEventListener *aListener);
};

#ifdef VBOX_WITH_XPCOM
NS_DECL_CLASSINFO(ProxyEventListener)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProxyEventListener, IEventListener)
NS_DECL_CLASSINFO(PassiveEventListener)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PassiveEventListener, IEventListener)
NS_DECL_CLASSINFO(EventSourceAggregator)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(EventSourceAggregator, IEventSource)
#endif

HRESULT EventSource::createListener(ComPtr<IEventListener> &aListener)
{
    ComObjPtr<PassiveEventListener> listener;

    HRESULT rc = listener.createObject();
    ComAssertMsgRet(SUCCEEDED(rc), ("Could not create wrapper object (%Rhrc)", rc),
                    E_FAIL);
    listener.queryInterfaceTo(aListener.asOutParam());
    return S_OK;
}

HRESULT EventSource::createAggregator(const std::vector<ComPtr<IEventSource> > &aSubordinates,
                                      ComPtr<IEventSource> &aResult)
{
    ComObjPtr<EventSourceAggregator> agg;

    HRESULT rc = agg.createObject();
    ComAssertMsgRet(SUCCEEDED(rc), ("Could not create aggregator (%Rhrc)", rc),
                    E_FAIL);

    rc = agg->init(aSubordinates);
    if (FAILED(rc))
        return rc;

    agg.queryInterfaceTo(aResult.asOutParam());
    return S_OK;
}

HRESULT EventSourceAggregator::init(const std::vector<ComPtr<IEventSource> >  aSourcesIn)
{
    HRESULT rc;

    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    rc = mSource.createObject();
    ComAssertMsgRet(SUCCEEDED(rc), ("Could not create source (%Rhrc)", rc),
                    E_FAIL);
    rc = mSource->init();
    ComAssertMsgRet(SUCCEEDED(rc), ("Could not init source (%Rhrc)", rc),
                    E_FAIL);

    for (size_t i = 0; i < aSourcesIn.size(); i++)
    {
        if (aSourcesIn[i] != NULL)
            mEventSources.push_back(aSourcesIn[i]);
    }

    /* Confirm a successful initialization */
    autoInitSpan.setSucceeded();

    return rc;
}

STDMETHODIMP EventSourceAggregator::CreateListener(IEventListener **aListener)
{
    return mSource->CreateListener(aListener);
}

STDMETHODIMP EventSourceAggregator::CreateAggregator(ComSafeArrayIn(IEventSource *, aSubordinates),
                                                     IEventSource **aResult)
{
    return mSource->CreateAggregator(ComSafeArrayInArg(aSubordinates), aResult);
}

STDMETHODIMP EventSourceAggregator::RegisterListener(IEventListener *aListener,
                                                     ComSafeArrayIn(VBoxEventType_T, aInterested),
                                                     BOOL aActive)
{
    CheckComArgNotNull(aListener);
    CheckComArgSafeArrayNotNull(aInterested);

    AutoCaller autoCaller(this);
    if (FAILED(autoCaller.rc()))
        return autoCaller.rc();

    HRESULT rc;

    ComPtr<IEventListener> proxy;
    rc = createProxyListener(aListener, proxy.asOutParam());
    if (FAILED(rc))
        return rc;

    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
         ++it)
    {
        ComPtr<IEventSource> es = *it;
        /* Register active proxy listener on real event source */
        rc = es->RegisterListener(proxy, ComSafeArrayInArg(aInterested), TRUE);
    }
    /* And add real listener on our event source */
    rc = mSource->RegisterListener(aListener, ComSafeArrayInArg(aInterested), aActive);

    rc = S_OK;

    return rc;
}

STDMETHODIMP EventSourceAggregator::UnregisterListener(IEventListener *aListener)
{
    CheckComArgNotNull(aListener);

    AutoCaller autoCaller(this);
    if (FAILED(autoCaller.rc()))
        return autoCaller.rc();

    HRESULT rc = S_OK;

    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);

    ComPtr<IEventListener> proxy;
    rc = getProxyListener(aListener, proxy.asOutParam());
    if (FAILED(rc))
        return rc;

    for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
         ++it)
    {
        ComPtr<IEventSource> es = *it;
        rc = es->UnregisterListener(proxy);
    }
    rc = mSource->UnregisterListener(aListener);

    return removeProxyListener(aListener);

}

STDMETHODIMP EventSourceAggregator::FireEvent(IEvent *aEvent,
                                              LONG aTimeout,
                                              BOOL *aProcessed)
{
    CheckComArgNotNull(aEvent);
    CheckComArgOutPointerValid(aProcessed);

    AutoCaller autoCaller(this);
    if (FAILED(autoCaller.rc()))
        return autoCaller.rc();

    HRESULT rc = S_OK;
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    /* Aggregator event source shall not have direct event firing, but we may
       wish to support aggregation chains */
    for (EventSourceList::const_iterator it = mEventSources.begin(); it != mEventSources.end();
         ++it)
    {
        ComPtr<IEventSource> es = *it;
        rc = es->FireEvent(aEvent, aTimeout, aProcessed);
        /* Current behavior is that aggregator's FireEvent() always succeeds,
           so that multiple event sources don't affect each other. */
        NOREF(rc);
    }

    return S_OK;
}

STDMETHODIMP EventSourceAggregator::GetEvent(IEventListener *aListener,
                                             LONG aTimeout,
                                             IEvent **aEvent)
{
    return mSource->GetEvent(aListener, aTimeout, aEvent);
}

STDMETHODIMP EventSourceAggregator::EventProcessed(IEventListener *aListener,
                                                   IEvent *aEvent)
{
    return mSource->EventProcessed(aListener, aEvent);
}

HRESULT EventSourceAggregator::createProxyListener(IEventListener *aListener,
                                                   IEventListener **aProxy)
{
    ComObjPtr<ProxyEventListener> proxy;

    HRESULT rc = proxy.createObject();
    ComAssertMsgRet(SUCCEEDED(rc), ("Could not create proxy (%Rhrc)", rc),
                    E_FAIL);

    rc = proxy->init(mSource);
    if (FAILED(rc))
        return rc;

    ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
    if (it != mListenerProxies.end())
        return setError(E_INVALIDARG,
                        tr("This listener already registered"));

    mListenerProxies.insert(ProxyListenerMap::value_type(aListener, proxy));

    proxy.queryInterfaceTo(aProxy);
    return S_OK;
}

HRESULT EventSourceAggregator::getProxyListener(IEventListener *aListener,
                                                IEventListener **aProxy)
{
    ProxyListenerMap::const_iterator it = mListenerProxies.find(aListener);
    if (it == mListenerProxies.end())
        return setError(E_INVALIDARG,
                        tr("This listener never registered"));

    (*it).second.queryInterfaceTo(aProxy);
    return S_OK;
}

HRESULT EventSourceAggregator::removeProxyListener(IEventListener *aListener)
{
    ProxyListenerMap::iterator it = mListenerProxies.find(aListener);
    if (it == mListenerProxies.end())
        return setError(E_INVALIDARG,
                        tr("This listener never registered"));

    mListenerProxies.erase(it);
    return S_OK;
}


text:
<?xml version="1.0" encoding="UTF-8"?>

	<item value="Default C source template" key="LastUsedSourceTemplate"/>
	<item value="Default C header template" key="LastUsedHeaderTemplate"/>

		<item value="0" key="pageIndex"/>

		<item value="true" key="includedBy"/>
		<item value="false" key="inactiveFilter"/>
		<item value="false" key="showFolders"/>
		<item value="false" key="systemFilter"/>

		<item value="384" key="size.width"/>
		<item value="180" key="size.height"/>

		<item value="0" key="pageIndex"/>

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
    xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
	http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
	default-autowire="byName">

    <!--== Engine Config ========================================================================================-->
    <bean id="ApplicationContextUtil" class="com.moomanow.core.common.context.ApplicationContextUtil"></bean>

	<import resource="persist-config/prj-jdbc.xml"/>
	<import resource="persist-config/prj-jpa.xml"/>
	<import resource="prj-config.xml"/>
	<import resource="prj-openid.xml"/>
<!--     <import resource="prj-config-ejb.xml"/> -->

</beans>


text:
//
//  HWAirTouchDeviceModel.m
//  AirTouch
//
//  Created by Carl on 7/21/15.
//  Copyright (c) 2015 Honeywell. All rights reserved.
//

#import "HWAirTouchDeviceModel.h"
#import "HttpClient.h"
#import "FilterModel.h"

@implementation HWAirTouchDeviceModel

#pragma mark - Inhierent Method

-(id)initWithDictionary:(NSDictionary *)params
{
    self = [super initWithDictionary:params];
    if (self) {
        self.pm25 = -1;
        self.isAlive = NO;

        self.scenarioMode = @"";
        self.fanSpeedStatus = @"";

        [self setupFilters];

        /*
         NOTE: this is not a good way to fix bug when device is really disabled on beginning!!!

         initialize it to be enabled because server does not send it firstly with AllLocations and PM25 API, only send when request RunStatus API
         */
        self.mobileCtrlFlags = @"ENABLED";

        self.maxPower = 7;
        self.pointNumber = 2;

        [self updateWithDictionary:params];
    }
    return self;
}

#pragma mark - Private Method

-(void)modifyDeviceStatus
{
    if ([self.scenarioMode isEqualToString:@"Manual"])
    {
        //self.fanSpeedStatus = currentControlSpeed;
    }
    else if ([self.scenarioMode isEqualToString:@"Sleep"])
    {
        self.fanSpeedStatus = @"Speed_1";
    }
    else if ([self.scenarioMode isEqualToString:@"Silent"])
    {
        self.fanSpeedStatus = @"Speed_2";
    }
    else if ([self.scenarioMode isEqualToString:@"Auto"])
    {
        if ([self.aqDisplayLevel isEqualToString:@"LEVEL_1"])
        {
            self.fanSpeedStatus = @"Speed_1";
        }
        else if ([self.aqDisplayLevel isEqualToString:@"LEVEL_2"])
        {
            self.fanSpeedStatus = @"Speed_3";
        }
        else if ([self.aqDisplayLevel isEqualToString:@"LEVEL_3"])
        {
            self.fanSpeedStatus = @"Speed_6";
        }
        else
        {
            self.fanSpeedStatus = @"Speed_3";
        }
    }
    else if ([self.scenarioMode isEqualToString:@"QuickClean"])
    {
        self.fanSpeedStatus = @"Speed_7";
    }
}

#pragma mark - HWDeviceControllable
-(NSString *)getDisplayMode
{
    NSString *mode = @"";

    if (!self.isAlive)
    {
        mode = NSLocalizedString(ALLDEVICES_MODE_OFFLINE, nil);
        return mode;
    }

    if ([self.scenarioMode isEqualToString:@"Manual"])
    {
        NSInteger speed = [self getFanSpeed];
        if (speed > 7)
        {
            speed = 7;
        }

        switch (speed)
        {
            case 7:
            case 6:
                mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_HIGH, nil);
                break;

            case 5:
            case 4:
            case 3:
                mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_MIDDLE, nil);
                break;

            case 2:
            case 1:
                mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_LOW, nil);
                break;

            case -1:
            default:
                mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_STOP, nil);
                break;
        }
    }
    else
    {
        if ([self.scenarioMode isEqualToString:@"Sleep"])
        {
            mode = NSLocalizedString(ALLDEVICES_MODE_SLEEP, nil);
        }
        else if([self.scenarioMode isEqualToString:@"Auto"])
        {
            mode = NSLocalizedString(ALLDEVICES_MODE_AUTO, nil);
        }
        //else if([self.scenarioMode isEqualToString:@"Quick"])
        else if([self.scenarioMode isEqualToString:@"QuickClean"])
        {
            mode = NSLocalizedString(ALLDEVICES_MODE_QUICK, nil);
        }
        else if([self.scenarioMode isEqualToString:@"Silent"])
        {
            mode = NSLocalizedString(ALLDEVICES_MODE_SILENT, nil);
        }
        else if([self.scenarioMode isEqualToString:@"Off"])
        {
            mode = NSLocalizedString(ALLDEVICES_MODE_OFF, nil);
        }
        else // no language handle to other mode by now!!!
        {
            mode = self.scenarioMode;
        }

    }

    return mode;
}

-(NSString *)getTargetTransitionDisplayMode
{
    NSString *mode = @"";

    if (!self.isAlive)
    {
        mode = NSLocalizedString(ALLDEVICES_MODE_OFFLINE, nil);
        return mode;
    }

    switch (self.targetStatus) {
        case ATDeviceStatusNormal:
            break;
        case ATDeviceStatusPowerOn:
            mode = NSLocalizedString(ALLDEVICES_MODE_AUTO, nil);
            break;
        case ATDeviceStatusPowerOff:
            mode = NSLocalizedString(ALLDEVICES_MODE_OFF, nil);
            break;
        case ATDeviceStatusSleep:
            mode = NSLocalizedString(ALLDEVICES_MODE_SLEEP, nil);
            break;
        case ATDeviceStatusAuto:
            mode = NSLocalizedString(ALLDEVICES_MODE_AUTO, nil);
            break;
        case ATDeviceStatusQuick:
            mode = NSLocalizedString(ALLDEVICES_MODE_QUICK, nil);
            break;
        case ATDeviceStatusSilent:
            mode = NSLocalizedString(ALLDEVICES_MODE_SILENT, nil);
            break;
        case ATDeviceStatusSpeed1:
        case ATDeviceStatusSpeed2:
            mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_LOW, nil);
            break;
        case ATDeviceStatusSpeed3:
        case ATDeviceStatusSpeed4:
        case ATDeviceStatusSpeed5:
            mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_MIDDLE, nil);
            break;
        case ATDeviceStatusSpeed6:
        case ATDeviceStatusSpeed7:
            mode = NSLocalizedString(ALLDEVICES_MODE_SPEED_HIGH, nil);
            break;
        default:
            break;
    }
    return mode;
}

-(NSString *)getFanSpeedFromTargetMode
{
    NSString *fanSpeed = nil;
    switch (self.targetStatus) {
        case ATDeviceStatusNormal:
            break;
        case ATDeviceStatusPowerOn:
            fanSpeed = @"Speed_1";
            break;
        case ATDeviceStatusPowerOff:
            break;
        case ATDeviceStatusSleep:
            fanSpeed = @"Speed_1";
            break;
        case ATDeviceStatusAuto:
            fanSpeed = @"Speed_1";
            break;
        case ATDeviceStatusQuick:
            fanSpeed = @"Speed_7";
            break;
        case ATDeviceStatusSilent:
            fanSpeed = @"Speed_2";
            break;
        case ATDeviceStatusSpeed1:
            fanSpeed = @"Speed_1";
            break;
        case ATDeviceStatusSpeed2:
            fanSpeed = @"Speed_2";
            break;
        case ATDeviceStatusSpeed3:
            fanSpeed = @"Speed_3";
            break;
        case ATDeviceStatusSpeed4:
            fanSpeed = @"Speed_4";
            break;
        case ATDeviceStatusSpeed5:
            fanSpeed = @"Speed_5";
            break;
        case ATDeviceStatusSpeed6:
            fanSpeed = @"Speed_6";
            break;
        case ATDeviceStatusSpeed7:
            fanSpeed = @"Speed_7";
            break;
        default:
            break;
    }
    return fanSpeed;
}

- (void)changeToStatusValue:(ATDeviceStatus)status {
    switch (status) {
        case ATDeviceStatusNormal:
            break;
        case ATDeviceStatusPowerOn:
            self.scenarioMode = @"Auto";
            self.fanSpeedStatus = @"Speed_1";
            break;
        case ATDeviceStatusPowerOff:
            self.scenarioMode = @"Off";
            self.fanSpeedStatus = nil;
            break;
        case ATDeviceStatusSleep:
            self.scenarioMode = @"Sleep";
            self.fanSpeedStatus = @"Speed_1";
            break;
        case ATDeviceStatusAuto:
            self.scenarioMode = @"Auto";
            self.fanSpeedStatus = @"Speed_1";
            break;
        case ATDeviceStatusQuick:
            self.scenarioMode = @"QuickClean";
            self.fanSpeedStatus = @"Speed_7";
            break;
        case ATDeviceStatusSilent:
            self.scenarioMode = @"Silent";
            self.fanSpeedStatus = @"Speed_2";
            break;
        case ATDeviceStatusSpeed1:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_1";
            break;
        case ATDeviceStatusSpeed2:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_2";
            break;
        case ATDeviceStatusSpeed3:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_3";
            break;
        case ATDeviceStatusSpeed4:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_4";
            break;
        case ATDeviceStatusSpeed5:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_5";
            break;
        case ATDeviceStatusSpeed6:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_6";
            break;
        case ATDeviceStatusSpeed7:
            self.scenarioMode = @"Manual";
            self.fanSpeedStatus = @"Speed_7";
            break;
        default:
            break;
    }
}

#pragma mark - Filters
- (void)setupFilters {
    NSMutableArray *filterArray = [NSMutableArray array];

    FilterModel *filter1 = [[FilterModel alloc] init];
    filter1.filterID = 0;
    filter1.sortNO = @"1";
    filter1.name = NSLocalizedString(PRE_FILTER, nil);
    filter1.progressColorRed = 193;
    filter1.progressColorGreen = 123;
    filter1.progressColorBlue = 181;
    filter1.infoTitle = NSLocalizedString(PRE_FILTER, nil);
    filter1.infoContent = NSLocalizedString(PRE_DESCRIPTION, nil);
    filter1.buyLink = FILTERURL_1;

    FilterModel *filter2 = [[FilterModel alloc] init];
    filter2.filterID = 1;
    filter2.sortNO = @"2";
    filter2.name = NSLocalizedString(HPF_FILTER, nil);
    filter2.progressColorRed = 213;
    filter2.progressColorGreen = 90;
    filter2.progressColorBlue = 103;
    filter2.infoTitle = NSLocalizedString(HPF_FILTER, nil);
    filter2.infoContent = NSLocalizedString(HPF_DESCRIPTION, nil);
    filter2.buyLink = FILTERURL_2;

    FilterModel *filter3 = [[FilterModel alloc] init];
    filter3.filterID = 2;
    filter3.sortNO = @"3";
    filter3.name = NSLocalizedString(HISIV_FILTER, nil);
    filter3.progressColorRed = 246;
    filter3.progressColorGreen = 207;
    filter3.progressColorBlue = 75;
    filter3.infoTitle = NSLocalizedString(HISIV_FILTER, nil);
    filter3.infoContent = NSLocalizedString(HISIV_DESCRIPTION, nil);
    filter3.buyLink = FILTERURL_3;

    [filterArray addObject:filter1];
    [filterArray addObject:filter2];
    [filterArray addObject:filter3];

    self.filters = filterArray;
}

-(BOOL)hasTvoc
{
    return NO;
}

@end


text:
import { yearsToQuarters } from 'date-fns/fp'
export default yearsToQuarters


text:
package tr.edu.firat.ceng.aml.assignments.decisiontree;

import java.io.IOException;
import java.util.Scanner;
import tr.edu.firat.ceng.aml.assignments.decisiontree.data.Dataset;
import tr.edu.firat.ceng.aml.assignments.decisiontree.tree.DecisionTree;
import tr.edu.firat.ceng.aml.assignments.decisiontree.tree.impl.DecisionTreeBuilder;
import tr.edu.firat.ceng.aml.assignments.decisiontree.tree.impl.GiniGainImpl;
import tr.edu.firat.ceng.aml.assignments.decisiontree.util.CSV2DatasetUtil;

/**
 *
 * @author Muhammed Olgun <141129113@ogrenci.firat.edu.tr>
 */
public class Main {

    public static void main(String[] args) {
        CSV2DatasetUtil util = new CSV2DatasetUtil();
        try {
            Dataset dataset = util.convert("iris.data");
            DecisionTreeBuilder builder = new DecisionTreeBuilder(new GiniGainImpl());
            DecisionTree tree = builder.build(dataset);
            Scanner scanner = new Scanner(System.in);
            while (true) {
                if (tree.getResult() != null) {
                    System.out.println(tree.getResult());
                    break;
                }
                System.out.println("Enter " + tree.getPropertyName() + " value:");
                tree = tree.next(scanner.nextDouble());
            }
        } catch (IOException ex) {
            System.out.println(ex);
        }
    }
}


text:
package com.bazaarvoice.ostrich.partition;

import com.bazaarvoice.ostrich.PartitionContext;
import com.bazaarvoice.ostrich.ServiceEndPoint;
import com.bazaarvoice.ostrich.ServicePool;

/**
 * Filters a set of end points based on a {@link PartitionContext} object.
 */
public interface PartitionFilter {
    /**
     * Filters a set of end points based on a {@link PartitionContext} object.
     *
     * @param endPoints A collection of end points.  Known (or suspected) bad end points have been removed.
     * @param partitionContext The {@link com.bazaarvoice.ostrich.PartitionContext} object passed to the
     *                         {@link ServicePool#execute} method.
     * @return A collection of end points that may service the specified partition.  This might be the same object
     *         passed in the {@code endPoints} argument if all end points may service the specified partition.
     */
    Iterable<ServiceEndPoint> filter(Iterable<ServiceEndPoint> endPoints, PartitionContext partitionContext);
}


text:
namespace gpopt
{
typedef CHashMap<CExpression, CExpression, CExpression::HashValue,
				 CUtils::Equals, CleanupRelease<CExpression>,
				 CleanupRelease<CExpression> >
	ExprPredToExprPredPartMap;

//---------------------------------------------------------------------------
//	@class:
//		CLogicalSelect
//
//	@doc:
//		Select operator
//
//---------------------------------------------------------------------------
class CLogicalSelect : public CLogicalUnary
{
private:
	// private copy ctor
	CLogicalSelect(const CLogicalSelect &);

	ExprPredToExprPredPartMap *m_phmPexprPartPred;

	// table descriptor
	CTableDescriptor *m_ptabdesc;

public:
	// ctor
	explicit CLogicalSelect(CMemoryPool *mp);

	// ctor
	CLogicalSelect(CMemoryPool *mp, CTableDescriptor *ptabdesc);

	// dtor
	virtual ~CLogicalSelect();

	// ident accessors
	virtual EOperatorId
	Eopid() const
	{
		return EopLogicalSelect;
	}

	virtual const CHAR *
	SzId() const
	{
		return "CLogicalSelect";
	}

	// return table's descriptor
	CTableDescriptor *
	Ptabdesc() const
	{
		return m_ptabdesc;
	}

	//-------------------------------------------------------------------------------------
	// Derived Relational Properties
	//-------------------------------------------------------------------------------------

	// derive output columns
	virtual CColRefSet *DeriveOutputColumns(CMemoryPool *, CExpressionHandle &);

	// dervive keys
	virtual CKeyCollection *DeriveKeyCollection(
		CMemoryPool *mp, CExpressionHandle &exprhdl) const;

	// derive max card
	virtual CMaxCard DeriveMaxCard(CMemoryPool *mp,
								   CExpressionHandle &exprhdl) const;

	// derive constraint property
	virtual CPropConstraint *
	DerivePropertyConstraint(CMemoryPool *mp, CExpressionHandle &exprhdl) const
	{
		return PpcDeriveConstraintFromPredicates(mp, exprhdl);
	}

	// derive table descriptor
	virtual CTableDescriptor *
	DeriveTableDescriptor(CMemoryPool *,  // mp
						  CExpressionHandle &exprhdl) const
	{
		return exprhdl.DeriveTableDescriptor(0);
	}

	// compute partition predicate to pass down to n-th child
	virtual CExpression *PexprPartPred(CMemoryPool *mp,
									   CExpressionHandle &exprhdl,
									   CExpression *pexprInput,
									   ULONG child_index) const;

	//-------------------------------------------------------------------------------------
	// Transformations
	//-------------------------------------------------------------------------------------

	// candidate set of xforms
	virtual CXformSet *PxfsCandidates(CMemoryPool *) const;

	//-------------------------------------------------------------------------------------
	//-------------------------------------------------------------------------------------
	//-------------------------------------------------------------------------------------

	// return true if operator can select a subset of input tuples based on some predicate,
	virtual BOOL
	FSelectionOp() const
	{
		return true;
	}

	// conversion function
	static CLogicalSelect *
	PopConvert(COperator *pop)
	{
		GPOS_ASSERT(NULL != pop);
		GPOS_ASSERT(EopLogicalSelect == pop->Eopid());

		return reinterpret_cast<CLogicalSelect *>(pop);
	}

	// derive statistics
	virtual IStatistics *PstatsDerive(CMemoryPool *mp,
									  CExpressionHandle &exprhdl,
									  IStatisticsArray *stats_ctxt) const;

};	// class CLogicalSelect

}  // namespace gpopt

#endif	// !GPOS_CLogicalSelect_H

// EOF


text:
using System;
using UnityEditor;
using UnityEngine;

namespace JustLogic.Editor.JLGUI.Drawers
{
    [ParameterDrawer(typeof(Vector3))]
    public class Vector3Drawer : ParameterDrawerBase
    {
        public override bool Initialize(DrawerInitArgs args, object value, IDrawContext context)
        {
            Layout = ParameterDrawerLayout.VerticalRoot;
            return base.Initialize(args, value, context);
        }

        protected override void DrawLabel(GUIContent label, bool isAppendedToHorizontal, ref bool hasVerticalOutline, IDrawContext context)
        {
            label = null;
            base.DrawLabel(label, isAppendedToHorizontal, ref hasVerticalOutline, context);
        }

        protected override bool OnDraw(IDrawContext context, bool isNewAreaStarted)
        {
            var value = (Vector3)Value;
            context.BeginLook(true);
            try
            {
                const string autoLabel = "Vector3";
                string label = InitArgs.Label != null ? InitArgs.Label.text : autoLabel;
                if (string.IsNullOrEmpty(label))
                    label = autoLabel;
                object newValue = EditorGUILayout.Vector3Field(label, value);
                if (!newValue.Equals(value))
                {
                    Value = newValue;
                    return true;
                }
                return false;
            }
            finally { context.EndLook(); }

        }
    }
}


text:
require "spec_helper"

describe Mongoid::Errors::DeleteRestriction do

  describe "#message" do

    let(:error) do
      described_class.new(Person.new, :drugs)
    end

    it "contains the problem in the message" do
      expect(error.message).to include(
        "Cannot delete Person because of dependent 'drugs'."
      )
    end

    it "contains the summary in the message" do
      expect(error.message).to include(
        "When defining 'drugs' with a :dependent => :restrict,"
      )
    end

    it "contains the resolution in the message" do
      expect(error.message).to include(
        "Don't attempt to delete the parent Person when it has children"
      )
    end
  end
end


text:
namespace dotless.Core.Parser.Tree
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Exceptions;
    using Infrastructure;
    using Infrastructure.Nodes;
    using Utils;
    using Plugins;

    public class MixinCall : Node
    {
        public List<NamedArgument> Arguments { get; set; }
        public Selector Selector { get; set; }
        public bool Important { get; set; }

        public MixinCall(NodeList<Element> elements, List<NamedArgument> arguments, bool important)
        {
            Important = important;
            Selector = new Selector(elements);
            Arguments = arguments;
        }

        public override Node Evaluate(Env env)
        {
            var closures = env.FindRulesets(Selector);
            if (closures == null)
                throw new ParsingException(Selector.ToCSS(env).Trim() + " is undefined", Location);

            env.Rule = this;

            var rules = new NodeList();

            if (PreComments)
                rules.AddRange(PreComments);

            var rulesetList = closures.ToList();

            // To address bug https://github.com/dotless/dotless/issues/136, where a mixin and ruleset selector may have the same name, we
            // need to favour matching a MixinDefinition with the required Selector and only fall back to considering other Ruleset types
            // if no match is found.
            // However, in order to support having a regular ruleset with the same name as a parameterized
            // mixin (see https://github.com/dotless/dotless/issues/387), we need to take argument counts into account, so we make the
            // decision after evaluating for argument match.

            var mixins = rulesetList.Where(c => c.Ruleset is MixinDefinition).ToList();

            var defaults = new List<Closure>();

            bool foundMatches = false, foundExactMatches = false, foundDefaultMatches = false;
            foreach (var closure in mixins)
            {
                var ruleset = (MixinDefinition)closure.Ruleset;
                var matchType = ruleset.MatchArguments(Arguments, env);
                if (matchType == MixinMatch.ArgumentMismatch)
                {
                    continue;
                }

                if (matchType == MixinMatch.Default) {
                    defaults.Add(closure);
                    foundDefaultMatches = true;

                    continue;
                }

                foundMatches = true;

                if (matchType == MixinMatch.GuardFail)
                {
                    continue;
                }

                foundExactMatches = true;

                try
                {
                    var closureEnvironment = env.CreateChildEnvWithClosure(closure);
                    rules.AddRange(ruleset.Evaluate(Arguments, closureEnvironment).Rules);
                }
                catch (ParsingException e)
                {
                    throw new ParsingException(e.Message, e.Location, Location);
                }
            }

            if (!foundExactMatches && foundDefaultMatches) {
                foreach (var closure in defaults) {
                    try {
                        var closureEnvironment = env.CreateChildEnvWithClosure(closure);
                        var ruleset = (MixinDefinition) closure.Ruleset;
                        rules.AddRange(ruleset.Evaluate(Arguments, closureEnvironment).Rules);
                    } catch (ParsingException e) {
                        throw new ParsingException(e.Message, e.Location, Location);
                    }
                }
                foundMatches = true;
            }

            if (!foundMatches)
            {
                var regularRulesets = rulesetList.Except(mixins);

                foreach (var closure in regularRulesets)
                {
                    if (closure.Ruleset.Rules != null)
                    {
                        var nodes = new NodeList(closure.Ruleset.Rules);
                        NodeHelper.ExpandNodes<MixinCall>(env, nodes);

                        rules.AddRange(nodes);
                    }

                    foundMatches = true;
                }
            }

            if (PostComments)
                rules.AddRange(PostComments);

            env.Rule = null;

            if (!foundMatches)
            {
                var message = String.Format("No matching definition was found for `{0}({1})`",
                                            Selector.ToCSS(env).Trim(),
                                            Arguments.Select(a => a.Value.ToCSS(env)).JoinStrings(env.Compress ? "," : ", "));
                throw new ParsingException(message, Location);
            }

            rules.IsReference = IsReference;
            foreach (var rule in rules) {
                rule.IsReference = IsReference;
            }

            if (Important)
            {
                return MakeRulesImportant(rules);
            }

            return rules;
        }

        public override void Accept(IVisitor visitor)
        {
            Selector = VisitAndReplace(Selector, visitor);

            foreach (var a in Arguments)
            {
                a.Value = VisitAndReplace(a.Value, visitor);
            }
        }

        private Ruleset MakeRulesetImportant(Ruleset ruleset)
        {
            return new Ruleset(ruleset.Selectors, MakeRulesImportant(ruleset.Rules)).ReducedFrom<Ruleset>(ruleset);
        }

        private NodeList MakeRulesImportant(NodeList rules)
        {
            var importantRules = new NodeList();
            foreach (var node in rules)
            {
                if (node is MixinCall)
                {
                    var original = (MixinCall)node;
                    importantRules.Add(new MixinCall(original.Selector.Elements, new List<NamedArgument>(original.Arguments), true).ReducedFrom<MixinCall>(node));
                }
                else if (node is Rule)
                {
                    importantRules.Add(MakeRuleImportant((Rule) node));
                }
                else if (node is Ruleset)
                {
                    importantRules.Add(MakeRulesetImportant((Ruleset) node));
                }
                else
                {
                    importantRules.Add(node);
                }
            }
            return importantRules;
        }

        private Rule MakeRuleImportant(Rule rule)
        {
            var valueNode = rule.Value;
            var value = valueNode as Value;
            value = value != null
                        ? new Value(value.Values, "!important").ReducedFrom<Value>(value)
                        : new Value(new NodeList {valueNode}, "!important");

            var importantRule = new Rule(rule.Name, value).ReducedFrom<Rule>(rule);
            return importantRule;
        }
    }
}


text:
#ifndef __MACH_TEGRA_SYSTEM_H
#define __MACH_TEGRA_SYSTEM_H

#include <mach/iomap.h>

extern void (*arch_reset)(char mode, const char *cmd);

static inline void arch_idle(void)
{
}

#endif


text:
require_relative '../../test_helper'

class StateWithValidInheritedMethodCallForCurrentStateTest < StateMachinesTest
  def setup
    @superclass = Class.new do
      def speed(arg = nil)
        [arg]
      end
    end
    @klass = Class.new(@superclass)
    @machine = StateMachines::Machine.new(@klass, initial: :idling)
    @ancestors = @klass.ancestors
    @state = @machine.state(:idling)
    @state.context do
      def speed(arg = nil)
        [arg] + super(2)
      end
    end

    @object = @klass.new
  end

  def test_should_not_raise_an_exception
    @state.call(@object, :speed, method_missing: lambda { fail })
  end

  def test_should_be_able_to_call_super
    assert_equal [1, 2], @state.call(@object, :speed, 1)
  end

  def test_should_allow_redefinition
    @state.context do
      def speed(arg = nil)
        [arg] + super(3)
      end
    end

    assert_equal [1, 3], @state.call(@object, :speed, 1)
  end
end


text:
// CheckedTree.cpp : implementation file
//

#include "stdafx.h"
#include "Impact.h"
#include "CheckedTree.h"

#include "custommsgs.h" // user defined messages IDS
#include "ResultsTree.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCheckedTree

CCheckedTree::CCheckedTree()
{
}

CCheckedTree::~CCheckedTree()
{
}

BEGIN_MESSAGE_MAP(CCheckedTree, CTreeCtrl)
	//{{AFX_MSG_MAP(CCheckedTree)
	ON_WM_CREATE()
	ON_WM_LBUTTONDOWN()
	ON_WM_KEYDOWN()
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCheckedTree message handlers

int CCheckedTree::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CTreeCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;

	m_StateIcons.Create( IDB_STATES, 13, 1, RGB(255,255,255) );
	SetImageList( &m_StateIcons, TVSIL_STATE );
	return 0;
}

void CCheckedTree::OnLButtonDown(UINT nFlags, CPoint point)
{
	UINT uFlags=0;
	HTREEITEM hti = HitTest(point,&uFlags);

	if(uFlags & TVHT_ONITEMSTATEICON) {
		int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
		if(iImage>0) {
			SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1),
						TVIS_STATEIMAGEMASK );
			// notify parent that state has been changed
			CWnd* pParent = GetParent();
			if(pParent!=NULL) pParent->SendMessage(WM_TREEITEMSTATECHANGED,NULL,(LPARAM)hti);
			return;
		}
	}

	CTreeCtrl::OnLButtonDown(nFlags, point);
}

void CCheckedTree::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if(nChar == VK_SPACE) {
		HTREEITEM hti = GetSelectedItem();
		int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
		if(iImage > 0) {
			SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1),
						TVIS_STATEIMAGEMASK );

			// notify parent that state has been changed
			CWnd* pParent = GetParent();
			if(pParent!=NULL) pParent->SendMessage(WM_TREEITEMSTATECHANGED,NULL,(LPARAM)hti);
			return;
		}
	}

	CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CCheckedTree::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
{
	HTREEITEM hItem = GetSelectedItem();
	TVITEM item;
	item.hItem = hItem;
	item.mask = TVIF_PARAM;
	BOOL bWorked = GetItem(&item);
	if(bWorked) {
		CResultEntity* pEntity = (CResultEntity*)item.lParam;
		if(pEntity!=NULL) {
			CString* pszFile = pEntity->getFile();
			if(pszFile!=NULL && !pszFile->IsEmpty()) {
				// notify parent that state has been changed
				CWnd* pParent = GetParent();
				if(pParent!=NULL) pParent->SendMessage(WM_OPENINSTANCE,NULL,(LPARAM)pEntity);
			}
		}
	}
	*pResult = 0;
}


text:
"""
The network analyzer records the coherent response of the signal at the port
:code:`input` to a sinusoidal excitation of variable frequency sent to the
output selected in :code:`output_direct`.

.. note:: If :code:`output_direct='off'`, another module's input can be set
          to :code:`networkanalyzer` to test its response to a frequency sweep.

* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.amplitude`
  sets the amplitude of the sinusoidal excitation in Volts.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.start_freq`/:attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.stop_freq`
  define the frequency range over which a transfer function is recorded.
  Swapping the values of :code:`start_freq` and :code:`stop_freq` reverses the
  direction of the frequency sweep. Setting :code:`stop_freq = start_freq`
  enables the "zero-span" mode, where the coherent response at a constant
  frequency is recorded as a function of time.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.points`
  defines the number of frequency points in the recorded transfer function.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.rbw` is
  the cutoff frequency of the low-pass filter after demodulation. Furthermore,
  the time :math:`\\tau` spent to record each point is
  :math:`\\tau=\\texttt{average_per_point} / \\texttt{rbw}`.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.average_per_point`:
  Each point is averaged inside the FPGA before being retrieved by the
  client computer that runs PyRPL. You should increase this parameter or
  decrease :code:`rbw` if the communication time between the Red Pitaya and
  the client computer limits the acquisition speed.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.acbandwidth`
  is the cutoff frequency of a high-pass filter applied to the input before
  demodulation. A setting of zero disables the high-pass filter.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.logscale`
  enables the use of a logarithmic scale for the frequency axis, resulting in
  a logarithmic distribution of the frequency points as well.
* :attr:`~pyrpl.software_modules.network_analyzer.NetworkAnalyzer.infer_open_loop_tf`
  applies the transformation :math:`T \\rightarrow \\frac{T}{1+T}` to the displayed
  transfer function to correct for the effect of a closed feedback loop
  (not implemented at the moment).
"""

from .base_module_widget import ModuleWidget
from.acquisition_module_widget import AcquisitionModuleWidget

from qtpy import QtCore, QtWidgets
import pyqtgraph as pg
from time import time
import numpy as np
import sys

class NaWidget(AcquisitionModuleWidget):
    """
    Network Analyzer Tab.
    """
    starting_update_rate =  0.2 # this would be a good idea to change this number dynamically when the curve becomes
    CHUNK_SIZE = 500
    # more and more expensive to display.

    def init_gui(self):
        """
        Sets up the gui
        """
        #self.main_layout = QtWidgets.QVBoxLayout()
        self.init_main_layout(orientation="vertical")
        self.init_attribute_layout()
        self.button_layout = QtWidgets.QHBoxLayout()
        #self.setLayout(self.main_layout)
        self.setWindowTitle("NA")
        self.win = pg.GraphicsWindow(title="Magnitude")

        self.label_benchmark = pg.LabelItem(justify='right')
        self.win.addItem(self.label_benchmark, row=1,col=0)
        self._last_benchmark_value = np.nan

        self.win_phase = pg.GraphicsWindow(title="Phase")
        self.plot_item = self.win.addPlot(row=1, col=0, title="Magnitude (dB)")
        self.plot_item_phase = self.win_phase.addPlot(row=1, col=0,
                                                      title="Phase (deg)")
        self.plot_item_phase.setXLink(self.plot_item)
        self.button_single = QtWidgets.QPushButton("Run single")
        self.button_single.my_label = "Single"
        self.button_continuous = QtWidgets.QPushButton("Run continuous")
        self.button_continuous.my_label = "Continuous"
        self.button_stop = QtWidgets.QPushButton('Stop')

        self.button_save = QtWidgets.QPushButton("Save curve")

        self.chunks = [] #self.plot_item.plot(pen='y')
        self.chunks_phase = []
        self.main_layout.addWidget(self.win)
        self.main_layout.addWidget(self.win_phase)

        aws = self.attribute_widgets
        self.attribute_layout.removeWidget(aws["trace_average"])
        self.attribute_layout.removeWidget(aws["curve_name"])

        #self.button_layout.addWidget(aws["trace_average"])
        #self.button_layout.addWidget(aws["curve_name"])

        super(NaWidget, self).init_gui()
        #self.button_layout.addWidget(self.button_single)
        #self.button_layout.addWidget(self.button_continuous)
        #self.button_layout.addWidget(self.button_stop)
        #self.button_layout.addWidget(self.button_save)
        #self.main_layout.addLayout(self.button_layout)

        #self.button_single.clicked.connect(self.run_single_clicked)
        #self.button_continuous.clicked.connect(self.run_continuous_clicked)
        #self.button_stop.clicked.connect(self.button_stop_clicked)
        #self.button_save.clicked.connect(self.save_clicked)

        self.arrow = pg.ArrowItem()
        self.arrow.setVisible(False)
        self.arrow_phase = pg.ArrowItem()
        self.arrow_phase.setVisible(False)
        self.plot_item.addItem(self.arrow)
        self.plot_item_phase.addItem(self.arrow_phase)
        self.last_updated_point = 0
        self.last_updated_time = 0
        #self.display_state(self.module.running_state)
        self.update_running_buttons()
        self.update_period = self.starting_update_rate # also modified in clear_curve.

        # Not sure why the stretch factors in button_layout are not good by
        # default...
        #self.button_layout.setStretchFactor(self.button_single, 1)
        #self.button_layout.setStretchFactor(self.button_continuous, 1)
        #self.button_layout.setStretchFactor(self.button_stop, 1)
        #self.button_layout.setStretchFactor(self.button_save, 1)
        self.x_log_toggled() # Set the axis in logscale if it has to be

    def autoscale(self):
        """
        log_mode = self.module.logscale
        self.plot_item.setLogMode(x=log_mod, y=None) # this seems also needed
        self.plot_item_phase.setLogMode(x=log_mod, y=None)
        """
        self.plot_item.setRange(xRange=[self.module.start_freq, self.module.stop_freq])
        self.plot_item_phase.setRange(xRange=[self.module.start_freq, self.module.stop_freq])

    def clear_curve(self):
        """
        Clear all chunks
        """
        self.update_period = self.starting_update_rate  # let's assume update of curve takes 50 ms
        while(True):
            try:
                chunk = self.chunks.pop()
                chunk_phase = self.chunks_phase.pop()
                chunk.clear()
                chunk_phase.clear()
            except IndexError:
                break
        self.label_benchmark.setText("")

    def x_log_toggled(self):
        """
        change x_log of axis
        """
        log_mod = self.module.logscale
        self.plot_item.setLogMode(x=log_mod, y=None) # this seems also needed
        self.plot_item_phase.setLogMode(x=log_mod, y=None)
        for chunk, chunk_phase in zip(self.chunks, self.chunks_phase):
            chunk.setLogMode(xMode=log_mod, yMode=None)
            chunk_phase.setLogMode(xMode=log_mod, yMode=None)

    def scan_finished(self):
        """
        if in run continuous, needs to redisplay the number of averages
        """
        self.update_current_average()
        self.update_point(self.module.points-1, force=True) # make sure all points in the scan are updated

    def set_benchmark_text(self, text):
        self.label_benchmark.setText(text)

    def update_point(self, index, force=False):
        """
        To speed things up, the curves are plotted by chunks of
        self.CHUNK_SIZE points. All points between last_updated_point and
        index will be redrawn.
        """
        # APP.processEvents()  # Give hand back to the gui since timer intervals might be very short
        last_chunk_index = self.last_updated_point//self.CHUNK_SIZE
        current_chunk_index = index//self.CHUNK_SIZE

        rate = self.module.measured_time_per_point
        if not np.isnan(rate) and self._last_benchmark_value != rate:
            theory = self.module.time_per_point
            self.set_benchmark_text("ms/pt: %.1f (theory: %.1f)"%(
                                                             rate*1000,
                                                             theory*1000))

        if force or (time() - self.last_updated_time > self.update_period):
            #  if last update time was a long time ago,
            #  update plot, otherwise we would spend more time plotting things than acquiring data...
            for chunk_index in range(last_chunk_index, current_chunk_index+1):
                self.update_chunk(chunk_index) # eventually several chunks to redraw
            self.last_updated_point = index
            self.last_updated_time = time()

            # draw arrow
            cur = self.module.current_point - 1
            visible = self.module.last_valid_point != cur + 1
            logscale = self.module.logscale
            freq = self.module.data_x[cur]
            xpos = np.log10(freq) if logscale else freq
            if cur > 0:
                self.arrow.setPos(xpos,
                                  self._magnitude(self.module.data_avg[
                                                      cur]))
                self.arrow.setVisible(visible)
                self.arrow_phase.setPos(xpos,
                                        self._phase(
                                            self.module.data_avg[cur]))
                self.arrow_phase.setVisible(visible)

    def _magnitude(self, data):
        return 20. * np.log10(np.abs(data)+sys.float_info.epsilon)

    def _phase(self, data):
        return np.angle(data, deg=True)

    def update_attribute_by_name(self, name, new_value_list):
        super(NaWidget, self).update_attribute_by_name(name, new_value_list)
        if name == "running_state":
            #self.display_state(self.module.running_state)
            self.update_running_buttons()

    def update_chunk(self, chunk_index):
        """
        updates curve # chunk_index with the data from the module
        """
        while len(self.chunks) <= chunk_index: # create as many chunks as needed to reach chunk_index (in principle only
            # one curve should be missing at most)
            chunk = self.plot_item.plot(pen='y')
            chunk_phase = self.plot_item_phase.plot(pen=None, symbol='o')
            self.chunks.append(chunk)
            self.chunks_phase.append(chunk_phase)
            log_mod = self.module.logscale
            chunk.setLogMode(xMode=log_mod, yMode=None)
            chunk_phase.setLogMode(xMode=log_mod, yMode=None)

        sl = slice(max(0, self.CHUNK_SIZE * chunk_index - 1),
                   min(self.CHUNK_SIZE * (chunk_index + 1),
                       self.module.last_valid_point),
                       1) # make sure there is an overlap between slices
        data = self.module.data_avg[sl]
        x = np.real(self.module.data_x[sl])
        self.chunks[chunk_index].setData(x, self._magnitude(data))
        self.chunks_phase[chunk_index].setData(x, self._phase(data))

    #def run_continuous_clicked(self):
    #    """
    #    launches a continuous run
    #    """
    #    if str(self.button_continuous.text()).startswith("Pause"):
    #        self.module.pause()
    #    else:
    #        self.module.continuous()

    #def run_single_clicked(self):
    #    """
    #    launches a single acquisition
    #    """
    #    if str(self.button_single.text()).startswith("Pause"):
    #        self.module.pause()
    #    else:
    #        self.module.single_async()

    #def save_clicked(self):
    #    """
    #    Save the current curve.
    #    """
    #    self.module.save_curve()

    def display_state(self, running_state):
        """
        Displays one of the possible states
        "running_continuous", "running_single", "paused_continuous", "paused_single", "stopped"
        """
        if not running_state in ["running_continuous",
                                 "running_single",
                                 "paused",
                                 "stopped"]:
            raise ValueError("Na running_state should be either "
                             "running_continuous, "
                             "running_single, "
                             "paused or "
                             "stopped")
        if running_state=="running_continuous":
            self.button_single.setEnabled(False)
            self.button_single.setText("Run single")
            self.button_continuous.setEnabled(True)
            self.button_continuous.setText("Pause")
            return
        if running_state== "running_single":
            self.button_single.setEnabled(True)
            self.button_single.setText("Pause")
            self.button_continuous.setEnabled(False)
            self.button_continuous.setText("Run continuous")
            return
        if running_state == "paused":
            self.button_continuous.setText("Resume continuous")
            self.button_single.setText("Run single")
            self.button_continuous.setEnabled(True)
            self.button_single.setEnabled(False)
            return
        if running_state == "stopped":
            self.button_continuous.setText("Run continuous")
            self.button_single.setText("Run single")
            self.button_continuous.setEnabled(True)
            self.button_single.setEnabled(True)
            return

    #def button_stop_clicked(self):
    #    """
    #    Going to stop will impose a setup_average before next run.
    #    """
    #    self.module.stop()

class MyGraphicsWindow(pg.GraphicsWindow):
    def __init__(self, title, parent_widget):
        super(MyGraphicsWindow, self).__init__(title)
        self.parent_widget = parent_widget
        self.setToolTip("IIR transfer function: \n"
                        "----------------------\n"
                        "CTRL + Left click: add one more pole. \n"
                        "SHIFT + Left click: add one more zero\n"
                        "Left Click: select pole (other possibility: click on the '+j' labels below the graph)\n"
                        "Left/Right arrows: change imaginary part (frequency) of the current pole or zero\n"
                        "Up/Down arrows; change the real part (width) of the current pole or zero. \n"
                        "Poles are represented by 'X', zeros by 'O'")

    def mousePressEvent(self, *args, **kwds):
        event = args[0]
        try:
            modifier = int(event.modifiers())
            it = self.getItem(0, 0)
            pos = it.mapToScene(event.pos()) #  + it.vb.pos()
            point = it.vb.mapSceneToView(pos)
            x, y = point.x(), point.y()
            x = 10 ** x
            new_z = -100 - 1.j * x
            if modifier==QtCore.Qt.CTRL:
                self.parent_widget.module.poles += [new_z]
                self.parent_widget.attribute_widgets['poles'].set_selected(-1)
            if modifier == QtCore.Qt.SHIFT:
                self.parent_widget.module.zeros += [new_z]
                self.parent_widget.attribute_widgets['zeros'].set_selected(-1)
        except BaseException as e:
            self.parent_widget.module._logger.error(e)
        finally:
            return super(MyGraphicsWindow, self).mousePressEvent(*args, **kwds)