/*  umplayer, GUI front-end for mplayer.
    Copyright (C) 2010 Ori Rejwan

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTemporaryFile>
#include <QDebug>
#include "downloadfile.h"

#define MOD_TIME(x) ((x+86400000)%86400000)

DownloadFile::DownloadFile(QString site, QFile *fileToDownload, QObject *parent):
    QObject(parent), speed(0)
{
    url = site;    
    manager = new QNetworkAccessManager(this);
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*)));
    getRequest(url);
    file = fileToDownload;
    file->open(QFile::ReadWrite);
}

void DownloadFile::getRequest(QString url)
{    
    QNetworkRequest req(QUrl::fromEncoded(url.toAscii(), QUrl::StrictMode));
    reply = manager->get(req);
    qDebug() << req.url().toEncoded();
    connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloaded(qint64,qint64)));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(emitError(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(metaDataChanged()), this, SLOT(gotMetaData()));    
}

void DownloadFile::downloaded(qint64 bytesReceived, qint64 total)
{
    totalSize = total;
    completed = bytesReceived;
    QTime current = QTime::currentTime();
    QPair<QTime, qint64> head = lastReceivedBytes.head();    
    if(MOD_TIME(lastReceivedBytes.last().first.msecsTo(current)) >= 1000)
    {
        lastReceivedBytes.enqueue(qMakePair(current, bytesReceived));
        speed = (bytesReceived - head.second)*1000/ MOD_TIME(head.first.msecsTo(current));
    }
    if(MOD_TIME(lastReceivedBytes.head().first.msecsTo(current)) >= 5000)
    {
        lastReceivedBytes.dequeue();
    }    
    emit progress(qRound(bytesReceived*100.0/total), total);
    file->write(static_cast<QNetworkReply*>(sender())->readAll());
    if(bytesReceived == total)
    {
        if(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl().isEmpty())
        {
            file->close();
            emit downloadFinished(reply->error() != QNetworkReply::NoError);
            deleteLater();
        }
    }
    updateFooterText();
}

void DownloadFile::updateFooterText()
{
    qint64 remainingTime = -1;
    QString remainingTimeString;
    if(speed > 0 )
        remainingTime = (totalSize - completed)/speed + 1 ;
    if(remainingTime == -1)
        remainingTimeString = "unknown time remaining - ";
    else
    {
        bool flag = false; // if hour is there but not minutes
        if(remainingTime > 3600)
        {
            remainingTimeString += QString::number(remainingTime / 3600) + " hours, ";
            remainingTime = remainingTime % 3600;
            flag = true;
        }
        if(remainingTime > 60 || flag)
        {
            remainingTimeString += QString("%1 minutes, ").arg(remainingTime / 60, 2, 10, QChar('0'));
            remainingTime = remainingTime % 60;
        }
        remainingTimeString += QString("%1 seconds remaining - ").arg(remainingTime, 2, 10, QChar('0'));
    }

    if(totalSize > 1048576 )
    {
        remainingTimeString += QString::number(completed / (qreal)1048576, 'f', 2);
        remainingTimeString += " of ";
        remainingTimeString += QString::number(totalSize / (qreal)1048576, 'f', 2);
        remainingTimeString += " MB ";
    }
    else if(totalSize > 1024 )
    {
        remainingTimeString += QString::number(completed / (qreal)1024, 'f', 2);
        remainingTimeString += " of ";
        remainingTimeString += QString::number(totalSize / (qreal)1024, 'f', 2);
        remainingTimeString += " KB ";
    }
    else if(totalSize >= 0)
    {
        remainingTimeString += QString::number(completed);
        remainingTimeString += " of ";
        remainingTimeString += QString::number(totalSize);
        remainingTimeString += " bytes ";
    }
    else
    {
        remainingTimeString += QString::number(completed);
        remainingTimeString += " of ";
        remainingTimeString += " Unknown size ";
    }
    remainingTimeString += "(";
    if(speed > 1048576 )
    {
        remainingTimeString += QString::number(speed/ (qreal)1048576, 'f', 2);
        remainingTimeString += " MB/sec)";
    }
    else if(totalSize > 1024 )
    {
        remainingTimeString += QString::number(speed/ (qreal)1024, 'f', 2);
        remainingTimeString += " KB/sec)";
    }
    else
    {
        remainingTimeString += QString::number(speed);
        remainingTimeString += " bytes/sec)";
    }
    emit downloadStatus(remainingTimeString);
}

void DownloadFile::gotMetaData()
{
    qDebug() << reply->rawHeaderList();
    if(reply->header(QNetworkRequest::ContentTypeHeader).isValid())
    {
        if( lastReceivedBytes.isEmpty())
            lastReceivedBytes.enqueue(qMakePair(QTime::currentTime(), 0LL));
    }
}

void DownloadFile::finished(QNetworkReply *rep)
{
    if(reply->error() != QNetworkReply::NoError) return;
    QUrl url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();        
    if(!url.isEmpty() && url != reply->request().url() )
    {
        reply->deleteLater();
        reply = 0;
        getRequest(url.toString());
    }
    else
    {
        reply->deleteLater();
        reply = 0;
    }
}

void DownloadFile::cancel()
{
    reply->disconnect(this, SLOT(downloaded(qint64,qint64)));
    reply->disconnect(this, SLOT(gotMetaData()));
    reply->abort();
    file->remove();
}

void DownloadFile::emitError(QNetworkReply::NetworkError error)
{
    qDebug() << error;
    emit errorOcurred((int)error);
}
