// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#pragma once

#include "cmakeconfigitem.h"

#include <utils/environment.h>
#include <utils/filepath.h>

#include <QHash>
#include <QVersionNumber>

namespace CMakeProjectManager::Internal {

namespace PresetsDetails {

class ValueStrategyPair {
public:
    std::optional<QString> value;
    enum class Strategy : bool { set, external };
    std::optional<Strategy> strategy;
};

class Warnings {
public:
    std::optional<bool> dev;
    std::optional<bool> deprecated;
    std::optional<bool> uninitialized = false;
    std::optional<bool> unusedCli = true;
    std::optional<bool> systemVars = false;
};

class Errors {
public:
    std::optional<bool> dev;
    std::optional<bool> deprecated;
};

class Debug {
public:
    std::optional<bool> output = false;
    std::optional<bool> tryCompile = false;
    std::optional<bool> find = false;
};

struct Trace {
    std::optional<QString> mode;
    std::optional<QString> format;
    std::optional<QStringList> source;
    std::optional<QString> redirect;
};

class Condition {
public:
    QString type;

    bool isNull() const { return type == "null"; }
    bool isConst() const  { return type == "const"; }
    bool isEquals() const  { return type == "equals"; }
    bool isNotEquals() const  { return type == "notEquals"; }
    bool isInList() const  { return type == "inList"; }
    bool isNotInList() const { return type == "notInList"; }
    bool isMatches() const { return type == "matches"; }
    bool isNotMatches() const { return type == "notMatches"; }
    bool isAnyOf() const { return type == "anyOf"; }
    bool isAllOf() const { return type == "allOf"; }
    bool isNot() const { return type == "not"; }

    bool evaluate() const;

    // const
    std::optional<bool> constValue;

    // equals, notEquals
    std::optional<QString> lhs;
    std::optional<QString> rhs;

    // inList, notInList
    std::optional<QString> string;
    std::optional<QStringList> list;

    // matches, notMatches
    std::optional<QString> regex;

    using ConditionPtr = std::shared_ptr<Condition>;

    // anyOf, allOf
    std::optional<std::vector<ConditionPtr>> conditions;

    // not
    std::optional<ConditionPtr> condition;
};

struct Output
{
    std::optional<bool> shortProgress;
    std::optional<QString> verbosity;
    std::optional<bool> debug;
    std::optional<bool> outputOnFailure;
    std::optional<bool> quiet;
    std::optional<Utils::FilePath> outputLogFile;
    std::optional<Utils::FilePath> outputJUnitFile;
    std::optional<bool> labelSummary;
    std::optional<bool> subprojectSummary;
    std::optional<int> maxPassedTestOutputSize;
    std::optional<int> maxFailedTestOutputSize;
    std::optional<QString> testOutputTruncation;
    std::optional<int> maxTestNameWidth;
};

struct Filter
{
    struct Include
    {
        std::optional<QString> name;
        std::optional<QString> label;
        std::optional<bool> useUnion;
        struct Index {
            std::optional<int> start;
            std::optional<int> end;
            std::optional<int> stride;
            std::optional<QList<int>> specificTests;
        };
        std::optional<Index> index;
    };
    struct Exclude
    {
        std::optional<QString> name;
        std::optional<QString> label;
        struct Fixtures
        {
            std::optional<QString> any;
            std::optional<QString> setup;
            std::optional<QString> cleanup;
        };
        std::optional<Fixtures> fixtures;
    };

    std::optional<Include> include;
    std::optional<Exclude> exclude;
};

struct Execution {
    std::optional<bool> stopOnFailure;
    std::optional<bool> enableFailover;
    std::optional<int> jobs;
    std::optional<Utils::FilePath> resourceSpecFile;
    std::optional<int> testLoad;
    std::optional<QString> showOnly;
    struct Repeat
    {
        QString mode;
        int count;
    };
    std::optional<Repeat> repeat;
    std::optional<bool> interactiveDebugging;
    std::optional<bool> scheduleRandom;
    std::optional<int> timeout;
    std::optional<QString> noTestsAction;
};

class ConfigurePreset {
public:
    void inheritFrom(const ConfigurePreset &other);

    QString name;
    Utils::FilePath fileDir;
    bool hidden = false;
    std::optional<QStringList> inherits;
    std::optional<Condition> condition;
    std::optional<QVariantMap> vendor;
    std::optional<QString> displayName;
    std::optional<QString> description;
    std::optional<QString> generator;
    std::optional<ValueStrategyPair> architecture;
    std::optional<ValueStrategyPair> toolset;
    std::optional<QString> toolchainFile;
    std::optional<QString> binaryDir;
    std::optional<QString> installDir;
    std::optional<Utils::FilePath> cmakeExecutable;
    std::optional<CMakeConfig> cacheVariables;
    std::optional<Utils::Environment> environment;
    std::optional<Warnings> warnings;
    std::optional<Errors> errors;
    std::optional<Debug> debug;
    std::optional<QString> graphviz;
    std::optional<Trace> trace;
};

class BuildPreset {
public:
    void inheritFrom(const BuildPreset &other);

    QString name;
    Utils::FilePath fileDir;
    bool hidden = false;
    std::optional<QStringList> inherits;
    std::optional<Condition> condition;
    std::optional<QVariantMap> vendor;
    std::optional<QString> displayName;
    std::optional<QString> description;
    std::optional<Utils::Environment> environment;
    std::optional<QString> configurePreset;
    bool inheritConfigureEnvironment = true;
    std::optional<int> jobs;
    std::optional<QStringList> targets;
    std::optional<QString> configuration;
    std::optional<bool> verbose;
    std::optional<bool> cleanFirst;
    std::optional<QStringList> nativeToolOptions;
};

class TestPreset {
public:
    void inheritFrom(const TestPreset &other);

    QString name;
    bool hidden = false;
    Utils::FilePath fileDir;
    std::optional<QStringList> inherits;
    std::optional<Condition> condition;
    std::optional<QVariantMap> vendor;
    std::optional<QString> displayName;
    std::optional<QString> description;
    std::optional<Utils::Environment> environment;
    std::optional<QString> configurePreset;
    bool inheritConfigureEnvironment = true;
    std::optional<QString> configuration;
    std::optional<QStringList> overwriteConfigurationFile;
    std::optional<Output> output;
    std::optional<Filter> filter;
    std::optional<Execution> execution;
};

} // namespace PresetsDetails

class PresetsData
{
public:
    int version = 0;
    bool havePresets = false;
    bool hasValidPresets = true;
    QVersionNumber cmakeMinimimRequired;
    std::optional<QVariantMap> vendor;
    std::optional<QStringList> include;
    Utils::FilePath fileDir;
    QList<PresetsDetails::ConfigurePreset> configurePresets;
    QList<PresetsDetails::BuildPreset> buildPresets;
    QList<PresetsDetails::TestPreset> testPresets;
};

class PresetsParser
{
    PresetsData m_presetsData;
public:
    bool parse(Utils::FilePath const &jsonFile, QString &errorMessage, int &errorLine);

    const PresetsData &presetsData() const;
};

std::optional<PresetsDetails::Condition> parseCondition(const QJsonValue &jsonValue);

} // CMakeProjectManager::Internal
