#include "nxtdeployparser.h"
#include <nxtconstants.h>

#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>

#include <utils/qtcassert.h>
#include <utils/temporarydirectory.h>

#include <QDir>
#include <QFile>

namespace Nxt {
namespace Internal {

namespace {
    const char * const ERROR_PATTERN("^(.*:)\\s*((Error:)\\s*(.*))$");
    const char * const WARNING_PATTERN("^(.*:)\\s*((Warning:)\\s*(.*))$");
}

NxtDeployParser::NxtDeployParser()
{
    setObjectName(QLatin1String("NxtDeployParser"));
    m_error.setPattern(QLatin1String(ERROR_PATTERN));
    QTC_CHECK(m_error.isValid());
    m_warning.setPattern(QLatin1String(WARNING_PATTERN));
    QTC_CHECK(m_error.isValid());
}

void NxtDeployParser::setWorkingDirectory(const QString &workingDirectory)
{
    addDirectory(workingDirectory);
    IOutputParser::setWorkingDirectory(workingDirectory);
}

bool NxtDeployParser::hasFatalErrors() const
{
    return (m_fatalErrorCount > 0) || IOutputParser::hasFatalErrors();
}

void NxtDeployParser::stdOutput(const QString &line)
{
    const QString trimmedLine = rightTrimmed(line);
    IOutputParser::stdOutput(line);
}

class Result {
public:
    Result() : isFatal(false), type(ProjectExplorer::Task::Error) { }

    QString description;
    bool isFatal;
    ProjectExplorer::Task::TaskType type;
};

static Result parseDescription(const QString &description)
{
    Result result;
    if (description.startsWith(QLatin1String("Warning: "), Qt::CaseInsensitive)) {
        result.description = QString("NXT Deploy: %1").arg(description.mid(9));
        result.type = ProjectExplorer::Task::Warning;
        result.isFatal = false;
    } else if (description.startsWith(QLatin1String("Error: "), Qt::CaseInsensitive)) {
        result.description = QString("NXT Deploy: %1").arg(description.mid(7));
        result.type = ProjectExplorer::Task::Error;
        result.isFatal = true;
    } else {
        result.description = QString("NXT Deploy: %1").arg(description);
        result.type = ProjectExplorer::Task::Error;
        result.isFatal = false;
    }
    return result;
}

void NxtDeployParser::stdError(const QString &line)
{
    const QString trimmedLine = rightTrimmed(line);
    QRegularExpressionMatch match;

    match = m_error.match(trimmedLine);
    if (match.hasMatch()) {
        flush();
        Result res = parseDescription(match.captured(2));
        if (res.isFatal)
            ++m_fatalErrorCount;
        if (!m_suppressIssues) {
            ProjectExplorer::Task task = ProjectExplorer::Task(res.type, res.description,
                             Utils::FileName() /* filename */, -1, /* line */
                             Core::Id(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
            taskAdded(task, 1, 0);
        }

        return;
    }

    match = m_warning.match(trimmedLine);
    if (match.hasMatch()) {
        flush();
        Result res = parseDescription(match.captured(2));
        if (res.isFatal)
            ++m_fatalErrorCount;
        if (!m_suppressIssues) {
            ProjectExplorer::Task task = ProjectExplorer::Task(res.type, res.description,
                             Utils::FileName() /* filename */, -1, /* line */
                             Core::Id(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
            taskAdded(task, 1, 0);
        }

        return;
    }

    IOutputParser::stdError(line);
}

void NxtDeployParser::addDirectory(const QString &dir)
{
    if (dir.isEmpty())
        return;
    m_directories.append(dir);
}

void NxtDeployParser::removeDirectory(const QString &dir)
{
    m_directories.removeOne(dir);
}

void NxtDeployParser::taskAdded(const ProjectExplorer::Task &task, int linkedLines, int skippedLines)
{
    ProjectExplorer::Task editable(task);

    if (task.type == ProjectExplorer::Task::Error) {
        // assume that all make errors will be follow up errors:
        m_suppressIssues = true;
    }

    QString filePath(task.file.toString());

    if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) {
        QList<QFileInfo> possibleFiles;
        foreach (const QString &dir, m_directories) {
            QFileInfo candidate(dir + QLatin1Char('/') + filePath);
            if (candidate.exists()
                && !possibleFiles.contains(candidate)) {
                possibleFiles << candidate;
            }
        }
        if (possibleFiles.size() == 1)
            editable.file = Utils::FileName(possibleFiles.first());
        // Let the Makestep apply additional heuristics (based on
        // files in ther project) if we can not uniquely
        // identify the file!
    }

    IOutputParser::taskAdded(editable, linkedLines, skippedLines);
}

} // namespace Internal
} // namespace Nxt
