#include <QtGui>

/*
    Writing this file iterator class is a bit cumbersome, but its core
    functionality (iterating over a 1-dimentional range with a given 
    block size) is something that can be done with a general range class.
    Such as class will probably find its way into a future Qt version.
*/
struct FileIterator
{
    typedef std::bidirectional_iterator_tag iterator_category;
    typedef int difference_type;
    typedef FileIterator value_type;
    typedef FileIterator * pointer;
    typedef FileIterator & reference; 

    FileIterator(const QString &fileName)
    { 
        this->fileName = fileName;
        fileSize = QFile(fileName).size();
        blockSize = 1024 * 10;

        begin = 0;
        end = clamp(begin + blockSize);
    }

    FileIterator()
    : begin(0), end(0) { }
    
    quint64 clamp(quint64 newValue)
    {
        return qMin(fileSize, newValue);
    }
    
    FileIterator operator*() { return *this; }
    bool operator != (const FileIterator &other) { if (other.end == 0) return begin != fileSize; else return begin != other.begin; }
    void operator++() { begin = clamp(begin + blockSize); end = clamp(end + blockSize);}
    FileIterator operator+(int inc) { FileIterator it(*this); it.begin = clamp(begin + inc * blockSize); it.end = clamp(end + inc * blockSize); return it; }

    QString fileName;
    quint64 blockSize;
    quint64 fileSize;
    quint64 begin;
    quint64 end;
};

struct Report
{
    QHash<QByteArray, int> hits;
//    QHash<QByteArray, int> bytes;
//    QHash<QByteArray, int> fourOhFours;
//    QHash<QByteArray, int> clients;
//    QHash<QByteArray, int> refs;
};

Report parse(const FileIterator &filePart)
{
    Report report;
    QRegExp articleRegExp("^/ongoing/When/\\d\\d\\dx/\\d\\d\\d\\d/\\d\\d/\\d\\d/[^ .]");
    
    qDebug() << "parse" << filePart.begin << filePart.end;
    
    QFile f(filePart.fileName);
    f.open(QIODevice::ReadOnly);
    f.seek(filePart.begin);
    QByteArray data = f.read(filePart.end - filePart.begin);
   
    QList<QByteArray> lines = data.split('\n');
    foreach (QByteArray line, lines) {
        QList<QByteArray> parts = line.split(' ');
        enum { Command = 5, Url = 6 };

        if (parts.count() < 10)
            continue;
        if (parts.at(Command) != "\"GET")
            continue;

        if (articleRegExp.indexIn(parts.at(Url)) != -1)
            ++report.hits[parts.at(Url)];
    }

    return report;
}

void merge(Report &final, const Report &intermediate)
{
    // Merge the hashes (unfortunately QHash::unite() does not merge values with the same key)
     QHash<QByteArray, int>::const_iterator it = intermediate.hits.constBegin();
     while (it != intermediate.hits.constEnd()) {
         final.hits[it.key()] += it.value();
        ++it;
     }
}

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    
    // Decrease the worker thread count by one since we'll be using the main thread as a worker thread.
    QThreadPool::globalInstance()->setMaxThreadCount(QThread::idealThreadCount() -1);

    FileIterator it("O.100k");
    FileIterator end;

    Report report = QtConcurrent::blockingMappedReduced(it, end, parse, merge);
//    QList<QPair<QByteArray, int> > sortedHits = report.hits.sortByValue(); No QHash::sortByValue() availvable yet :)

    qDebug() << report.hits;
}
