#include "context/EventData.h"
#include "context/EventDefinition.h"
#include "data/Event.h"
#include "event/ContextEventListener.h"
#include "event/ContextEventListenerSet.h"
#include "util/Util.h"
#include "util/Log.h"
#include "util/simpleobject/AgDate.h"
#include "AggreGateException.h"

EventData::EventData(EventDefinitionPtr definition)
{
    this->fireCount = 0;
    this->definition = definition;
    listeners = ContextEventListenerSetPtr(new ContextEventListenerSet());
}

void EventData::registerFiredEvent()
{
    fireCount++;
}

EventDefinitionPtr EventData::getDefinition()
{
    return definition;
}

boost::unordered_set<ContextEventListenerPtr>  EventData::getListeners()
{
    return listeners->getListeners();
}

boost::unordered_set<ContextEventListenerInfoPtr>  EventData::getListenersInfo()
{
    return listeners->getListenersInfo();
}

int64_t EventData::getFireCount()
{
    return fireCount;
}

bool EventData::addListener(ContextEventListenerPtr listener, bool weak)
{
    return listeners->addListener(listener, weak);
}

bool EventData::removeListener(ContextEventListenerPtr listener)
{
    return listeners->removeListener(listener);
}

void EventData::clearListeners()
{
    listeners->clear();
}

void EventData::dispatch(EventPtr event)
{
    try
    {
        boost::unordered_set<ContextEventListenerPtr> listenerSet = listeners->getListeners();

        if (Log::CONTEXT_EVENTS.isDebugEnabled())
        {
            LOG_CONTEXT_EVENTS_DEBUG("Dispatching event '" + event->toString().toUtf8() + "', " + AgString::fromInt(listenerSet.size()).toUtf8() + " listeners");
        }

        for (boost::unordered_set<ContextEventListenerPtr>::iterator el=listenerSet.begin(); el!=listenerSet.end();++el)
        {
            try {
                if (! (*el)->shouldHandle(event))
                {
                    if (Log::CONTEXT_EVENTS.isDebugEnabled())
                    {
                        LOG_CONTEXT_EVENTS_DEBUG("Listener '" + (*el)->toString().toUtf8() + "' does not want to handle event: " + event->toString().toUtf8());
                    }
                    continue;
                }

                if (Log::CONTEXT_EVENTS.isDebugEnabled())
                {
                    LOG_CONTEXT_EVENTS_DEBUG("Listener '" + (*el)->toString().toUtf8() + "' is going to handle event: " + event->toString().toUtf8());
                }

                (*el)->handle(event);
            }
            catch (AggreGateException ex)
            {
                LOG_CONTEXT_EVENTS_WARN("Error handling event '" + event->toString().toUtf8() + "'");
            }
        }
    }
    catch (...)
    {
         LOG_CONTEXT_EVENTS_ERROR("Unexpected error occurred while dispatching event '" + event->toString().toUtf8() + "'");
    }
}

EventPtr EventData::store(EventPtr event, unsigned int  customMemoryStorageSize)
{
    unsigned int memoryStorateSize = customMemoryStorageSize != 0 ? customMemoryStorageSize : definition->getMemoryStorageSize();

    EventPtr duplicate = EventPtr();

    if (!memoryStorateSize) return duplicate;

    {
        boost::unique_lock<boost::mutex> lock(historyMutex);

        std::list<EventPtr>::iterator iterator = history.begin();

        while (iterator != history.end())
        {
            EventPtr cur = (*iterator);

            // Removing if expired
            if ((cur->getExpirationtime().get() != NULL) && (cur->getExpirationtime()->getValue() < boost::posix_time::microsec_clock::local_time()))
            {
                iterator = history.erase(iterator);
                continue;
            }

            // Adding for persistent storage if in-memory history size exceeded
            if (history.size() > memoryStorateSize)
            {
                iterator = history.erase(iterator);
                continue;
            }

            if (!event->getDeduplicationId().empty() &&  event->getDeduplicationId() == cur->getDeduplicationId())
            {
                if (duplicate.get() != NULL)
                {
                    LOG_CONTEXT_EVENTS_WARN("Event history of event " + event->toString().toUtf8() + " contains more than one duplicate with ID: " + event->getDeduplicationId().toUtf8());
                }

                duplicate = cur;
            }
        }
    }

    if (duplicate == NULL)
    {
        history.push_back(event);
        return EventPtr();
    }
    else
    {
        LOG_CONTEXT_EVENTS_DEBUG("Found duplicate of event " + event->toString().toUtf8() + " (duplicate ID: " + event->getDeduplicationId().toUtf8() + "): " + duplicate->toString().toUtf8());
        duplicate->setCreationtime(event->getCreationtime());
        duplicate->setCount(duplicate->getCount() + 1);
        return duplicate;
    }
}

std::list<EventPtr> EventData::getHistory()
{
    return this->history;
}

AgString EventData::toString()
{
    AgString ss = definition->toString() + " - " + AgString::fromInt(listeners->size()) + " listeners";
    return ss;
}

int EventData::compareTo(Comparable *d)
{
    EventData* other = static_cast<EventData*>(d);
    return definition->compareTo(other->getDefinition().get());
}
