/*
 * Copyright (C) 2012 Victor Carbune (victor@rosedu.org)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "GenericEventQueue.h"

#include "Element.h"
#include "Event.h"
#include <wtf/CurrentTime.h>
#include <wtf/MainThread.h>
#include <wtf/MemoryMonitor.h>

namespace WebCore {

GenericEventQueue::GenericEventQueue(EventTarget& owner)
	: m_owner(&owner)
{
}

GenericEventQueue::~GenericEventQueue() = default;

GenericEventQueueWithElementOwner::GenericEventQueueWithElementOwner(Element& owner)
: GenericEventQueue(owner)
, m_weakOwner(makeWeakPtr(owner))
{
}

GenericEventQueueWithElementOwner::~GenericEventQueueWithElementOwner()
{
}

EventTarget* GenericEventQueueWithElementOwner::owner() const
{
	return m_weakOwner.get();
}

void GenericEventQueue::enqueueEvent(PassRefPtr<Event> event)
{
    if (m_isClosed)
        return;

    if (event->target() == owner())
        event->setTarget(nullptr);

    m_pendingEvents.append(event);

    if (m_isSuspended)
        return;

    pendingQueues().append(makeWeakPtr(*this));
    auto& sharedTimer = GenericEventQueue::sharedTimer();
    if (!sharedTimer.isActive())
        sharedTimer.startOneShot(0);
}

StaticTimer& GenericEventQueue::sharedTimer()
{
    ASSERT(isMainThread());
    static StaticTimer timer(GenericEventQueue::sharedTimerFired);
    return timer;
}

void GenericEventQueue::sharedTimerFired()
{
    ASSERT(!sharedTimer().isActive());
    ASSERT(!queues.isEmpty());

    double startTime = monotonicallyIncreasingTime();

    // Copy the pending events first because we don't want to process synchronously the new events
    // queued by the JS events handlers that are executed in the loop below.
    Deque<WeakPtr<GenericEventQueue>> queuedEvents;
    queuedEvents.swap(pendingQueues());
    while (!queuedEvents.isEmpty()) {
        WeakPtr<GenericEventQueue> queue = queuedEvents.takeFirst();
        if (!queue)
            continue;
        queue->dispatchOneEvent();
    }

    int ms = static_cast<int>((monotonicallyIncreasingTime() - startTime) * 1000);
    if(ms > 15)
    	WTF::logMessage(tryMakeString("GenericEventQueue::sharedTimerFired() takes ", String::number(ms), "ms"));
}

void GenericEventQueue::dispatchAllEvents()
{
    auto& sharedTimer = GenericEventQueue::sharedTimer();
    if (sharedTimer.isActive())
    	sharedTimer.stop();

    sharedTimerFired();
}

Deque<WeakPtr<GenericEventQueue> >& GenericEventQueue::pendingQueues()
{
    ASSERT(isMainThread());
    static Deque<WeakPtr<GenericEventQueue>>* queues = new Deque<WeakPtr<GenericEventQueue>>();
    return *queues;
}

void GenericEventQueue::dispatchOneEvent()
{
    ASSERT(!m_pendingEvents.isEmpty());

    RefPtr<EventTarget> protect(owner());
    RefPtr<Event> event = m_pendingEvents.takeFirst();
    EventTarget* target = event->target() ? event->target() : owner();
    if(target)
    	target->dispatchEvent(event.release());
}

void GenericEventQueue::close()
{
    m_isClosed = true;

    weakPtrFactory().revokeAll();
    m_pendingEvents.clear();
}

void GenericEventQueue::cancelAllEvents()
{
	weakPtrFactory().revokeAll();
    m_pendingEvents.clear();
}

bool GenericEventQueue::hasPendingEvents() const
{
    return !m_pendingEvents.isEmpty();
}

void GenericEventQueue::suspend()
{
    ASSERT(!m_isSuspended);
    m_isSuspended = true;
    weakPtrFactory().revokeAll();
}

void GenericEventQueue::resume()
{
    ASSERT(m_isSuspended);
    m_isSuspended = false;

    if (m_pendingEvents.isEmpty())
        return;

    for (unsigned i = 0; i < m_pendingEvents.size(); ++i)
        pendingQueues().append(makeWeakPtr(*this));

    if (!sharedTimer().isActive())
        sharedTimer().startOneShot(0);
}

}
