#define DEBUG_PREFIX "InfoPlug"

#include "amarok.h"
#include "amarokconfig.h"
#include "collectiondb.h"
#include "config.h"
#include "debug.h"
#include "enginecontroller.h"
#include "playlist.h"
#include "infoplug.h"
#include "statusbar.h"
#include <fcntl.h>
#include <sys/poll.h>

InfoPlug* InfoPlug::instance()
{
    static InfoPlug infoplug;
    return &infoplug;
}

InfoPlug::InfoPlug() :
    EngineObserver(EngineController::instance()),
    m_enabled(false),
    m_sock((int) 0)
{
}

void InfoPlug::shutdown()
{
    m_enabled = false;
    while (m_sock) {
        sleep(5);
    }

    if (m_sock) {
        ::shutdown(m_sock, SHUT_RDWR);
        m_sock = false;
    }
    ThreadWeaver::instance()->abortAllJobsNamed("InfoPlugServer");
}

InfoPlug::~InfoPlug()
{
}

void InfoPlug::starter()
{
    m_enabled = true;
    ThreadWeaver::instance()->queueJob(new InfoPlugServer(this));
}


InfoPlugServer::InfoPlugServer(InfoPlug* parent) :
    Job("InfoPlugServer"),
    m_parent(parent)
{
}

InfoPlugServer::~InfoPlugServer()
{
    m_parent->m_enabled = false;

    if (m_parent->m_sock) {
        shutdown(m_parent->m_sock, SHUT_RDWR);
    }
}


bool InfoPlugServer::doJob()
{
    int bind;
    socklen_t clientAddrLen;
    struct sockaddr_in clientAddr, serverAddr;
    struct timeval timeout;
    QString info;
    int status, retval, opts;
    struct pollfd pfd;

    // Reset data
    char version[100];

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(5353);

    if (!m_parent->m_enabled) {
        return false;
    }

    m_parent->m_sock = ::socket(PF_INET, SOCK_DGRAM, 0);

    if (m_parent->m_sock == -1) {
        warning() << "InfoPlug: socket errno: " << errno << endl;
        return false;
    }

    bind = ::bind(m_parent->m_sock, (struct sockaddr*) &serverAddr, sizeof(serverAddr));
    if (bind == -1) {
        warning() << "InfoPlug: bind errno: " << errno << endl;
        return false;
    }

    opts = fcntl(m_parent->m_sock, F_GETFL);
    if (opts < 0) {
        perror("fcntl(F_GETFL)");
    }
    fcntl(m_parent->m_sock, F_SETFL, O_ASYNC | O_NONBLOCK);
    pfd.fd = m_parent->m_sock;
    pfd.events = POLLIN;

    while(true)
    {
        if (!m_parent->m_enabled) {
            if (m_parent->m_sock) {
                ::shutdown(m_parent->m_sock, SHUT_RDWR);
                m_parent->m_sock = false;
            }
            return false;
        } else {
            timeout.tv_sec = 1;
            timeout.tv_usec = 0;
            info = "";

            clientAddrLen = sizeof(clientAddr);

            retval = poll(&pfd, 1, 1000);
            if (retval < 0) {
                perror("poll()");
            } else if (retval) {

                if (recvfrom(m_parent->m_sock, NULL, 0, 0, (struct sockaddr *) &clientAddr, &clientAddrLen) == -1) {
                    warning() << "InfoPlug: recvfrom errno: " << errno << endl;
                    sleep(1);
                } else {
                    warning() << "============== InfoPlug: connection from: " << inet_ntoa(clientAddr.sin_addr) << endl;

                    /*
                     * Collect data here and put it in data.
                     */
                    const MetaBundle &bundle = EngineController::instance()->bundle();
                    QueryBuilder qb;
                    qb.addReturnValue(QueryBuilder::tabStats, QueryBuilder::valPlayCounter);
                    qb.addMatches(QueryBuilder::tabStats, bundle.url().path());

                    snprintf(version, 100, "Interface: InfoPlug\nVersion: %d.%d\n", INFOPLUG_VERSION_MA, INFOPLUG_VERSION_MI);
                    info.append(version);
                
                    status = EngineController::engine()->state();
                    if (status == Engine::Playing) info.append("Status: Playing\n"); // snprintf(plStatus, 1000, "Status: Playing\n");
                    else if (status == Engine::Paused) info.append("Status: Playing\n"); // snprintf(plStatus, 1000, "Status: Paused\n");
                    else info.append("Status: Stopped\n"); // snprintf(plStatus, 1000, "Status: Stopped\n");

                    if (status == Engine::Playing || status == Engine::Paused) {
                        info += "Artist: "+            bundle.artist();
                        info += "\nTitle: "+        bundle.title();
                        info += "\nAlbum: "+        bundle.album();
                        info += "\nTrack: "+        (bundle.track() ? QString::number(bundle.track()) : QString::null);
                        info += "\nTotal Time: "+    bundle.prettyLength();
                        info += "\nCurrent Time: "+    bundle.prettyLength(EngineController::engine()->position()/1000, true);
                        info += "\nGenre: "+        bundle.genre();
                        info += "\nYear: "+            QString::number(bundle.year());
                        info += "\nComment: "+        bundle.comment();
                        info += "\nBitrate: "+        bundle.prettyBitrate();
                        info += "\nSample Rate: "+    bundle.prettySampleRate();
                        info += "\nRating: "+        QString::number(CollectionDB::instance()->getSongRating(bundle.url().path()));
                        info += "\nScore: "+        QString::number(CollectionDB::instance()->getSongPercentage(bundle.url().path()));
                        info += "\nPlay Count: "+    QString::number(qb.run().first().toInt());
                        info += "\nVolume: "+        QString::number(EngineController::engine()->volume());
                        info += "\nURL: "+            bundle.url().url();
                        info += "\nCover Image: "+    CollectionDB::instance()->albumImage(bundle.artist(), bundle.album(), 0);
                    }
    
                    if (sendto(m_parent->m_sock, info.utf8(), info.length() + 1, 0,
                            (struct sockaddr *) &clientAddr, sizeof(clientAddr)) == -1) {
                        warning() << "InfoPlug: sendto errno: " << errno << endl;
                    }
                } /* recvfrom */
            } /* for poll */
        } /* Was enabled */
    } /* While-loop */

    return true;
}

void InfoPlugServer::completeJob()
{
    if (m_parent->m_sock) {
        ::shutdown(m_parent->m_sock, SHUT_RDWR);
        m_parent->m_sock = false;
    }
}

#include "infoplug.moc"