
#ifndef _WIN32
#include <unistd.h>
#else
#include <windows.h>
#define sleep Sleep
#endif
#include <time.h>

#include <poppler-qt4.h>
#include <poppler-form.h>

#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QMutex>
#include <QtCore/QThread>
#include <QtGui/QImage>

class SillyThread : public QThread
{
public:
    SillyThread(Poppler::Document* document, QObject* parent = 0);

    void run();

private:
    Poppler::Document* m_document;
    QVector< Poppler::Page* > m_pages;

};

class CrazyThread : public QThread
{
public:
    CrazyThread(uint seed, Poppler::Document* document, QMutex* annotationMutex, QObject* parent = 0);

    void run();

private:
    uint m_seed;
    Poppler::Document* m_document;
    QMutex* m_annotationMutex;

};

static Poppler::Page* loadPage(Poppler::Document* document, int index)
{
    Poppler::Page* page = document->page(index);

    if(page == 0)
    {
        qDebug() << "!Document::page";
        
        exit(EXIT_FAILURE);
    }

    return page;
}

static Poppler::Page* loadRandomPage(Poppler::Document* document)
{
    return loadPage(document, qrand() % document->numPages());
}

SillyThread::SillyThread(Poppler::Document* document, QObject* parent) : QThread(parent),
    m_document(document),
    m_pages()
{
    m_pages.reserve(m_document->numPages());

    for(int index = 0; index < m_document->numPages(); ++index)
    {
        m_pages.append(loadPage(m_document, index));
    }
}


void SillyThread::run()
{
    forever
    {
        foreach(Poppler::Page* page, m_pages)
        {
            QImage image = page->renderToImage();

            if(image.isNull())
            {
                qDebug() << "!Page::renderToImage";
                
                ::exit(EXIT_FAILURE);
            }
        }
    }
}

CrazyThread::CrazyThread(uint seed, Poppler::Document* document, QMutex* annotationMutex, QObject* parent) : QThread(parent),
    m_seed(seed),
    m_document(document),
    m_annotationMutex(annotationMutex)
{
}

void CrazyThread::run()
{
    typedef QScopedPointer< Poppler::Page > PagePointer;

    qsrand(m_seed);

    forever
    {
        if(qrand() % 2 == 0)
        {
            qDebug() << "search...";

            PagePointer page(loadRandomPage(m_document));

            page->search("c", Poppler::Page::CaseInsensitive);
            page->search("r", Poppler::Page::CaseSensitive);
            page->search("a", Poppler::Page::CaseInsensitive);
            page->search("z", Poppler::Page::CaseSensitive);
            page->search("y", Poppler::Page::CaseInsensitive);
        }

        if(qrand() % 2 == 0)
        {
            qDebug() << "links...";

            PagePointer page(loadRandomPage(m_document));

            QList< Poppler::Link* > links = page->links();

            qDeleteAll(links);
        }

        if(qrand() % 2 == 0)
        {
            qDebug() << "form fields...";

            PagePointer page(loadRandomPage(m_document));

            QList< Poppler::FormField* > formFields = page->formFields();

            qDeleteAll(formFields);
        }

        if(qrand() % 2 == 0)
        {
            qDebug() << "thumbnail...";

            PagePointer page(loadRandomPage(m_document));

            page->thumbnail();
        }

        if(qrand() % 2 == 0)
        {
            qDebug() << "text...";

            PagePointer page(loadRandomPage(m_document));

            page->text(QRectF(QPointF(), page->pageSizeF()));
        }

        if(qrand() % 2 == 0)
        {
            QMutexLocker mutexLocker(m_annotationMutex);
            
            qDebug() << "add annotation...";

            PagePointer page(loadRandomPage(m_document));

            Poppler::Annotation* annotation = 0;

            switch(qrand() % 3)
            {
            default:
            case 0:
                annotation = new Poppler::TextAnnotation(qrand() % 2 == 0 ? Poppler::TextAnnotation::Linked : Poppler::TextAnnotation::InPlace);
                break;
            case 1:
                annotation = new Poppler::HighlightAnnotation();
                break;
            case 2:
                annotation = new Poppler::InkAnnotation();
                break;
            }

            annotation->setBoundary(QRectF(0.0, 0.0, 0.5, 0.5));
            annotation->setContents("crazy");

            page->addAnnotation(annotation);

            delete annotation;
        }

        if(qrand() % 2 == 0)
        {
            QMutexLocker mutexLocker(m_annotationMutex);
            
            for(int index = 0; index < m_document->numPages(); ++index)
            {
                PagePointer page(loadPage(m_document, index));

                QList< Poppler::Annotation* > annotations = page->annotations();

                if(!annotations.isEmpty())
                {
                    qDebug() << "modify annotation...";

                    annotations.at(qrand() % annotations.size())->setBoundary(QRectF(0.5, 0.5, 0.25, 0.25));
                    annotations.at(qrand() % annotations.size())->setAuthor("foo");
                    annotations.at(qrand() % annotations.size())->setContents("bar");
                    annotations.at(qrand() % annotations.size())->setCreationDate(QDateTime::currentDateTime());
                    annotations.at(qrand() % annotations.size())->setModificationDate(QDateTime::currentDateTime());
                }

                qDeleteAll(annotations);

                if(!annotations.isEmpty())
                {
                    break;
                }
            }
        }

        if(qrand() % 2 == 0)
        {
            QMutexLocker mutexLocker(m_annotationMutex);
            
            for(int index = 0; index < m_document->numPages(); ++index)
            {
                PagePointer page(loadPage(m_document, index));

                QList< Poppler::Annotation* > annotations = page->annotations();

                if(!annotations.isEmpty())
                {
                    qDebug() << "remove annotation...";

                    page->removeAnnotation(annotations.takeAt(qrand() % annotations.size()));
                }

                qDeleteAll(annotations);

                if(!annotations.isEmpty())
                {
                    break;
                }
            }
        }

        if(qrand() % 2 == 0)
        {
            qDebug() << "fonts...";

            m_document->fonts();
        }
    }
}

int main(int argc, char** argv)
{
    if(argc < 5)
    {
        qDebug() << "usage: stress-threads-qt duration sillyCount crazyCount file(s)";
        
        return EXIT_FAILURE;
    }

    const int duration = atoi(argv[1]);
    const int sillyCount = atoi(argv[2]);
    const int crazyCount = atoi(argv[3]);
    
    qsrand(time(0));

    for(int argi = 4; argi < argc; ++argi)
    {
        const QString file = QFile::decodeName(argv[argi]);
        Poppler::Document* document = Poppler::Document::load(file);

        if(document == 0)
        {
            qDebug() << "Could not load" << file;            
            continue;
        }
        
        if(document->isLocked())
        {
            qDebug() << file << "is locked";
            continue;
        }
        
        for(int i = 0; i < sillyCount; ++i)
        {
            (new SillyThread(document))->start();
        }
        
        QMutex* annotationMutex = new QMutex();

        for(int i = 0; i < crazyCount; ++i)
        {
            (new CrazyThread(qrand(), document, annotationMutex))->start();
        }
    }

    sleep(duration);

    return EXIT_SUCCESS;
}
