#include "OscRouter.h"

#include <QtCore>
#include <QtCore/QDateTime>
#include <QtCore/QTimer>
#include <QtCore/QTime>

/*
 * number of seconds in 70 years,
 * the difference between the unix epoch and 
 * the current NTP epoch
*/
static const unsigned int difference_from_epoch = 2208988800;

#ifdef __GNUC__
#define _ATTRP __attribute__ ((__packed__))
#else
#define _ATTRP
#pragma pack
#endif
union  __ntp_timestamp {
    quint64 timestamp;
    struct {
    quint32 seconds _ATTRP;
    quint32 fraction _ATTRP;
    } parts;
};


OscRouter::OscRouter(int port) : 
    QThread(),
    _port(port),
    _sock(this)
{

}

OscRouter::~OscRouter()
{

}


void OscRouter::run()
{
    if(_sock.bind(_port, QUdpSocket::ShareAddress|QUdpSocket::ReuseAddressHint)) {
        qDebug("Bound to port %d", _port);
        while(_sock.waitForReadyRead()) {
            readPending();
        }
    } else {
        qFatal("Could not bind to UDP port %d", _port);
    }
}

void OscRouter::readPending()
{
    while(_sock.hasPendingDatagrams()) {
        int dsize = _sock.pendingDatagramSize();
        char* data = new char[dsize];
        QHostAddress* sender = new QHostAddress();
        quint16 senderport;

        qDebug("ready to read %d bytes", dsize);
        int size = _sock.readDatagram(data, dsize, sender, &senderport);
        if(size < 1) {
            delete[] data;
        } else {

        osc::ReceivedPacket p(data, size);
        if(p.IsBundle())
            dispatchBundle(new osc::ReceivedBundle(p), sender, senderport);
        else
            dispatchMessage(new osc::ReceivedMessage(p), sender, senderport);

        }
    }
}

void
OscRouter::dispatchBundle(const osc::ReceivedBundle* b,
        const QHostAddress* sender, int senderport)
{
    if(b->TimeTag() == 1L) { /* special value 1 in LSB, meaning NOW */
        for( osc::ReceivedBundle::const_iterator i = b->ElementsBegin(); 
				i != b->ElementsEnd(); ++i ){
            if( i->IsBundle() )
                dispatchBundle(new osc::ReceivedBundle(*i), sender, senderport);
            else
                dispatchMessage(new osc::ReceivedMessage(*i), sender, senderport);
        }
    } else {
        /* for now, I only work at second presicion */
        union __ntp_timestamp tt;
        tt.timestamp = b->TimeTag();
        QDateTime current = QDateTime::currentDateTime();
        if(tt.parts.seconds <= current.toTime_t()) {
            
        } else {
            //schedule for later
            bundle_schedule* s = new bundle_schedule();
            s->b = b;
            s->sender = sender;
            s->senderport = senderport;
            schedule.insert(tt.parts.seconds, s);
            scheduleTimer();
        }
    }
}

void OscRouter::scheduledBundle(bundle_schedule* s)
{
    for( osc::ReceivedBundle::const_iterator i = s->b->ElementsBegin(); 
			    i != s->b->ElementsEnd(); ++i ){
        if( i->IsBundle() )
            dispatchBundle(new osc::ReceivedBundle(*i), s->sender, s->senderport);
        else
            dispatchMessage(new osc::ReceivedMessage(*i), s->sender, s->senderport);
    } 
    delete s;
}

void OscRouter::scheduleTimer()
{
    QMap<quint32, bundle_schedule*>::iterator i = schedule.begin();
    QDateTime dt = QDateTime::currentDateTime();
    while(i != schedule.end()) {
        dt.setTime(QTime::currentTime());
        if(i.key() > dt.toTime_t())
            break;
        scheduledBundle(i.value());
        i = schedule.erase(i);
    }

    if(i != schedule.end()) {
        dt.setTime(QTime::currentTime());
        QTimer::singleShot( (i.key() - dt.toTime_t()) * 1000, this, SLOT(scheduleTimer()));
    }
}


void 
OscRouter::dispatchMessage
(const osc::ReceivedMessage* rmsg, const QHostAddress* sender, int senderport) 
{
    OscMessage* msg = new OscMessage();
    msg->setMessage(rmsg);
    msg->setSender(sender, senderport);
    QString adr(rmsg->AddressPattern());

    if(addressIsPattern(adr)) {
        QSet<AbstractOscMethod*>* set = matchPattern(adr);
        if(set->size() < 1)
            return;

        QSet<AbstractOscMethod*>::iterator i;
        for(i = set->begin(); i != set->end(); ++i) {
            (*i)->handleMessage(msg);
        }
     } else {
        if(addresses.contains(adr)) {
            //bool stat = QMetaObject::invokeMethod(addresses.value(adr), 
            //       "handleMessage",
            //        Qt::BlockingQueuedConnection,
            //        Q_ARG(OscMessage*, m));
            
            addresses.value(adr)->handleMessage(msg);
            qDebug("Dispatched %s", rmsg->AddressPattern());
        }
    }
}


bool
OscRouter::addressIsPattern(const QString& address) const
{
    return false;
}


QSet<AbstractOscMethod*>* 
OscRouter::matchPattern(const QString& pattern) const {
    return new QSet<AbstractOscMethod*>();
}

void
OscRouter::addMethod(const QString& address, AbstractOscMethod* ep)
{
    addresses.insert(address, ep);
}




//EOF OscRouter.cpp