/* This file is part of the KDE project
 * Copyright (C) 2009, 2012 Dag Andersen <danders@get2net.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

// clazy:excludeall=qstring-arg
#include "KPlatoRCPSPlugin.h"

#include "kptschedulerplugin_macros.h"
#include "kptdebug.h"

#include "KPlatoRCPSScheduler.h"

#include "kptproject.h"
#include "kptschedule.h"
#include <kptschedulerplugin.h>

#include <librcps.h>

#include <KLocalizedString>

#include <QApplication>

#ifndef PLAN_NOPLUGIN
PLAN_SCHEDULERPLUGIN_EXPORT(KPlatoRCPSPlugin, "planrcpsscheduler.json")
#endif

using namespace KPlato;

KPlatoRCPSPlugin::KPlatoRCPSPlugin(QObject * parent, const QVariantList &)
    : KPlato::SchedulerPlugin(parent)
{
    debugPlan<<rcps_version();
    m_granularities << (long unsigned int) 1 * 60 * 1000
                    << (long unsigned int) 15 * 60 * 1000
                    << (long unsigned int) 30 * 60 * 1000
                    << (long unsigned int) 60 * 60 * 1000;
}

KPlatoRCPSPlugin::~KPlatoRCPSPlugin()
{
}

QString KPlatoRCPSPlugin::description() const
{
    return xi18nc("@info:whatsthis", "<title>RCPS Scheduler</title>"
                    "<para>The Resource Constrained Project Scheduler (RCPS) focuses on scheduling"
                    " the project to avoid overbooking resources."
                    " It still respects task dependencies and also tries to fulfill time constraints."
                    " However, time constraints can make it very difficult to find a good solution,"
                    " so it may be preferable to use a different scheduler in these cases.</para>"
                );
}

int KPlatoRCPSPlugin::capabilities() const
{
    return SchedulerPlugin::AvoidOverbooking | SchedulerPlugin::ScheduleForward | SchedulerPlugin::ScheduleBackward;
}

ulong KPlatoRCPSPlugin::currentGranularity() const
{
    ulong v = m_granularities.value(m_granularity);
    return qMax(v, (ulong)60000); // minimum 1 min
}

void KPlatoRCPSPlugin::calculate(KPlato::Project &project, KPlato::ScheduleManager *sm, bool nothread)
{
    foreach (SchedulerThread *j, m_jobs) {
        if (j->manager() == sm) {
            return;
        }
    }
    sm->setScheduling(true);

    KPlatoRCPSScheduler *job = new KPlatoRCPSScheduler(&project, sm, currentGranularity());
    m_jobs << job;
    connect(job, SIGNAL(jobFinished(SchedulerThread*)), SLOT(slotFinished(SchedulerThread*)));

    project.changed(sm);

//     connect(this, SIGNAL(sigCalculationStarted(KPlato::Project*,KPlato::ScheduleManager*)), &project, SIGNAL(sigCalculationStarted(KPlato::Project*,KPlato::ScheduleManager*)));
//     connect(this, SIGNAL(sigCalculationFinished(KPlato::Project*,KPlato::ScheduleManager*)), &project, SIGNAL(sigCalculationFinished(KPlato::Project*,KPlato::ScheduleManager*)));

    connect(job, SIGNAL(maxProgressChanged(int)), sm, SLOT(setMaxProgress(int)));
    connect(job, SIGNAL(progressChanged(int)), sm, SLOT(setProgress(int)));

    if (nothread) {
        job->doRun();
    } else {
        job->start();
    }
}

void KPlatoRCPSPlugin::stopAllCalculations()
{
    foreach (SchedulerThread *s, m_jobs) {
        stopCalculation(s);
    }
}

void KPlatoRCPSPlugin::stopCalculation(SchedulerThread *sch)
{
    if (sch) {
         //FIXME: this should just call stopScheduling() and let the job finish "normally"
        disconnect(sch, SIGNAL(jobFinished(KPlatoRCPSScheduler*)), this, SLOT(slotFinished(KPlatoRCPSScheduler*)));
        sch->stopScheduling();
        // wait max 20 seconds.
        sch->mainManager()->setCalculationResult(ScheduleManager::CalculationStopped);
        if (! sch->wait(20000)) {
            sch->deleteLater();
            m_jobs.removeAt(m_jobs.indexOf(sch));
        }   else {
            slotFinished(sch);
        }
    }
}

void KPlatoRCPSPlugin::slotStarted(SchedulerThread */*job*/)
{
//    debugPlan<<"KPlatoRCPSPlugin::slotStarted:";
}

void KPlatoRCPSPlugin::slotFinished(SchedulerThread *j)
{
    KPlatoRCPSScheduler *job = static_cast<KPlatoRCPSScheduler*>(j);
    Project *mp = job->mainProject();
    ScheduleManager *sm = job->mainManager();
    //debugPlan<<"KPlatoRCPSPlugin::slotFinished:"<<mp<<sm<<job->isStopped();
    if (job->isStopped()) {
        sm->setCalculationResult(ScheduleManager::CalculationCanceled);
    } else {
        updateLog(job);
        Project *tp = job->project();
        ScheduleManager *tm = job->manager();
        updateProject(tp, tm, mp, sm);
        sm->setCalculationResult(ScheduleManager::CalculationDone);
    }
    sm->setScheduling(false);

    m_jobs.removeAt(m_jobs.indexOf(job));
    if (m_jobs.isEmpty()) {
        m_synctimer.stop();
    }
    emit sigCalculationFinished(mp, sm);

    disconnect(this, SIGNAL(sigCalculationStarted(KPlato::Project*,KPlato::ScheduleManager*)), mp, SIGNAL(sigCalculationStarted(KPlato::Project*,KPlato::ScheduleManager*)));
    disconnect(this, SIGNAL(sigCalculationFinished(KPlato::Project*,KPlato::ScheduleManager*)), mp, SIGNAL(sigCalculationFinished(KPlato::Project*,KPlato::ScheduleManager*)));

    job->deleteLater();
}


#include "KPlatoRCPSPlugin.moc"
