/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qdocgenerator.h"

#include <QtCore/qdir.h>

#include <iostream>

namespace QDocGenerator {

// See definition of idstring and licenseid in https://spdx.org/spdx-specification-21-web-version
static bool isSpdxLicenseId(const QString &str) {
    if (str.isEmpty())
        return false;
    for (auto iter(str.cbegin()); iter != str.cend(); ++iter) {
        const QChar c = *iter;
        if (!((c >= QLatin1Char('A') && c <= QLatin1Char('Z'))
              || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))
              || (c >= QLatin1Char('0') && c <= QLatin1Char('9'))
              || (c == QLatin1Char('-')) || (c == QLatin1Char('.'))))
            return false;
    }
    return true;
}

static QString languageJoin(const QStringList &list)
{
    QString result;
    for (int i = 0; i < list.size(); ++i) {
        QString delimiter = QStringLiteral(", ");
        if (i == list.size() - 1) // last item
            delimiter.clear();
        else if (list.size() == 2)
            delimiter = QStringLiteral(" and ");
        else if (list.size() > 2 && i == list.size() - 2)
            delimiter = QStringLiteral(", and "); // oxford comma
        result += list[i] + delimiter;
    }

    return result;
}

static void generate(QTextStream &out, const Package &package, const QDir &baseDir,
                     LogLevel logLevel)
{
    out << "/*!\n\n";
    for (const QString &part: package.qtParts)
        out << "\\ingroup attributions-" << part << "\n";

    if (package.qtParts.contains(QLatin1String("libs"))) {
        // show up in xxx-index.html page of module
        out << "\\ingroup attributions-" << package.qdocModule << "\n";
        // include in '\generatelist annotatedattributions'
        out << "\\page " << package.qdocModule << "-attribution-" << package.id
            << ".html attribution\n";
    } else {
        out << "\\page " << package.qdocModule << "-attribution-" << package.id
            << ".html \n";
    }

    out << "\\target " << package.id << "\n\n";
    out << "\\title " << package.name;
    if (!package.version.isEmpty())
        out << ", version " << package.version;
    out << "\n\n\\brief " << package.license << "\n\n";

    if (!package.description.isEmpty())
        out << package.description << "\n\n";

    if (!package.qtUsage.isEmpty())
        out << package.qtUsage << "\n\n";

    QStringList sourcePaths;
    if (package.files.isEmpty()) {
        sourcePaths << baseDir.relativeFilePath(package.path);
    } else {
        const QDir packageDir(package.path);
        for (const QString &filePath: package.files) {
            const QString absolutePath = packageDir.absoluteFilePath(filePath);
            sourcePaths << baseDir.relativeFilePath(absolutePath);
        }
    }

    out << "The sources can be found in " << languageJoin(sourcePaths) << ".\n\n";

    const bool hasPackageVersion = !package.version.isEmpty();
    const bool hasPackageDownloadLocation = !package.downloadLocation.isEmpty();
    if (!package.homepage.isEmpty()) {
        out << "\\l{" << package.homepage << "}{Project Homepage}";
        if (hasPackageVersion)
            out << ", ";
    }
    if (hasPackageVersion) {
        out << "upstream version: ";
        if (hasPackageDownloadLocation)
            out << "\\l{" << package.downloadLocation << "}{";
        out << package.version;
        if (hasPackageDownloadLocation)
            out << "}";
    }

    out << "\n\n";

    if (!package.copyright.isEmpty())
        out << "\n\\badcode\n" << package.copyright << "\n\\endcode\n\n";

    if (isSpdxLicenseId(package.licenseId) && package.licenseId != QLatin1String("NONE")) {
        out << "\\l{https://spdx.org/licenses/" << package.licenseId << ".html}"
            << "{" << package.license << "}.\n\n";
    } else if (package.licenseId.startsWith(QLatin1String("urn:dje:license:"))) {
        out << "\\l{https://enterprise.dejacode.com/licenses/public/" << package.licenseId.mid(16)
            << "/}{" << package.license << "}.\n\n";
    } else {
        out << package.license << ".\n\n";
    }

    foreach (const QString &licenseFile, package.licenseFiles) {
        QFile file(licenseFile);
        if (!file.open(QIODevice::ReadOnly)) {
            if (logLevel != SilentLog) {
                std::cerr << qPrintable(tr("Path %1 : cannot open license file %2.\n")
                                                .arg(QDir::toNativeSeparators(package.path))
                                                .arg(QDir::toNativeSeparators(licenseFile)));
                out << "*/\n";
            }
            return;
        }
        out << "\\badcode\n";
        out << QString::fromUtf8(file.readAll()).trimmed();
        out << "\n\\endcode\n\n";
    }
    out << "*/\n";
}

void generate(QTextStream &out, const QVector<Package> &packages, const QString &baseDirectory,
              LogLevel logLevel)
{
    if (logLevel == VerboseLog)
        std::cerr << qPrintable(tr("Generating qdoc file...")) << std::endl;

    QDir baseDir(baseDirectory);
    for (const Package &package : packages)
        generate(out, package, baseDir, logLevel);
}

} // namespace QDocGenerator
