/*
 * Copyright (c) 2020-2021 Valve Corporation
 * Copyright (c) 2020-2021 LunarG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Authors:
 * - Christophe Riccio <christophe@lunarg.com>
 */

#pragma once

#include "version.h"
#include "application.h"
#include "path_manager.h"

#include <QByteArray>

#include <array>
#include <vector>
#include <string>

enum OverrideFlag { OVERRIDE_FLAG_ACTIVE = (1 << 0), OVERRIDE_FLAG_SELECTED = (1 << 1), OVERRIDE_FLAG_PERSISTENT = (1 << 2) };

enum OverrideState {
    OVERRIDE_STATE_DISABLED = 0,
    OVERRIDE_STATE_GLOBAL_TEMPORARY = OVERRIDE_FLAG_ACTIVE,
    OVERRIDE_STATE_GLOBAL_PERSISTENT = OVERRIDE_FLAG_ACTIVE | OVERRIDE_FLAG_PERSISTENT,
    OVERRIDE_STATE_SELECTED_TEMPORARY = OVERRIDE_FLAG_ACTIVE | OVERRIDE_FLAG_SELECTED,
    OVERRIDE_STATE_SELECTED_PERSISTENT = OVERRIDE_FLAG_ACTIVE | OVERRIDE_FLAG_SELECTED | OVERRIDE_FLAG_PERSISTENT,
};

enum OverrideMode { OVERRIDE_MODE_ACTIVE = 0, OVERRIDE_MODE_LIST, OVERRIDE_MODE_PERISTENT };

enum LayoutState {
    LAYOUT_MAIN_GEOMETRY = 0,
    LAYOUT_MAIN_WINDOW_STATE,
    LAYOUT_MAIN_SPLITTER1,
    LAYOUT_MAIN_SPLITTER2,
    LAYOUT_MAIN_SPLITTER3,
    LAYOUT_LAYER_GEOMETRY,
    LAYOUT_LAYER_SPLITTER,
    LAYOUT_LAUNCHER_COLLAPSED,
    LAYOUT_LAUNCHER_NOT_CLEAR,

    LAYOUT_FIRST = LAYOUT_MAIN_GEOMETRY,
    LAYOUT_LAST = LAYOUT_LAUNCHER_NOT_CLEAR,
};

enum { LAYOUT_COUNT = LAYOUT_LAST - LAYOUT_FIRST + 1 };

enum Active {
    ACTIVE_CONFIGURATION = 0,
    ACTIVE_APPLICATION,

    ACTIVE_FIRST = ACTIVE_CONFIGURATION,
    ACTIVE_LAST = ACTIVE_APPLICATION,
};

enum { ACTIVE_COUNT = ACTIVE_LAST - ACTIVE_FIRST + 1 };

enum UserDefinedLayersPaths {
    USER_DEFINED_LAYERS_PATHS_ENV = 0,
    USER_DEFINED_LAYERS_PATHS_GUI,

    USER_DEFINED_LAYERS_PATHS_FIRST = USER_DEFINED_LAYERS_PATHS_ENV,  // VK_LAYER_PATH
    USER_DEFINED_LAYERS_PATHS_LAST = USER_DEFINED_LAYERS_PATHS_GUI,
};

enum { USER_DEFINED_LAYERS_PATHS_COUNT = USER_DEFINED_LAYERS_PATHS_LAST - USER_DEFINED_LAYERS_PATHS_FIRST + 1 };

enum LoaderMessageLevel {
    LAODER_MESSAGE_NONE = 0,
    LAODER_MESSAGE_ERROR,
    LAODER_MESSAGE_WARN,
    LAODER_MESSAGE_INFO,
    LAODER_MESSAGE_DEBUG,
    LAODER_MESSAGE_ALL,

    LAODER_MESSAGE_FIRST = LAODER_MESSAGE_NONE,
    LAODER_MESSAGE_LAST = LAODER_MESSAGE_ALL,
};

enum { LAODER_MESSAGE_COUNT = LAODER_MESSAGE_LAST - LAODER_MESSAGE_FIRST + 1 };

class Environment {
   public:
    Environment(PathManager& paths, const Version& api_version = Version::VKHEADER);
    ~Environment();

    enum ResetMode { DEFAULT = 0, CLEAR, SYSTEM };

    void Reset(ResetMode mode);

    bool Load();
    bool LoadApplications();
    bool Save() const;
    bool SaveApplications() const;

    void SelectActiveApplication(std::size_t application_index);
    int GetActiveApplicationIndex() const;
    bool HasOverriddenApplications() const;
    bool AppendApplication(const Application& application);
    bool RemoveApplication(std::size_t application_index);

    const std::vector<Application>& GetApplications() const { return applications; }
    const Application& GetActiveApplication() const;
    const Application& GetApplication(std::size_t application_index) const;
    Application& GetApplication(std::size_t application_index);

    const std::string& Get(Active active) const;
    void Set(Active active, const std::string& key);

    const QByteArray& Get(LayoutState state) const;
    void Set(LayoutState state, const QByteArray& data);

    bool UseOverride() const;
    bool UseApplicationListOverrideMode() const;
    bool UsePersistentOverrideMode() const;

    void SetMode(OverrideMode mode, bool enabled);

    LoaderMessageLevel GetLoaderMessage() const { return this->loader_message_level; }
    void SetLoaderMessage(LoaderMessageLevel level) { this->loader_message_level = level; }

    const bool running_as_administrator;  // Are we being "Run as Administrator"

    bool first_run;
    const Version api_version;

    bool AppendCustomLayerPath(const std::string& path);
    bool RemoveCustomLayerPath(const std::string& path);
    void ClearCustomLayerPath();
    const std::vector<std::string>& GetUserDefinedLayersPaths(UserDefinedLayersPaths user_defined_layers_paths_id) const {
        return user_defined_layers_paths[user_defined_layers_paths_id];
    }

    bool IsDefaultConfigurationInit(const std::string& configuration_filename) const;
    void InitDefaultConfiguration(const std::string& configuration_filename);

   private:
    Environment(const Environment&) = delete;
    Environment& operator=(const Environment&) = delete;

    Version version;
    OverrideState override_state;
    LoaderMessageLevel loader_message_level;

    std::array<std::string, ACTIVE_COUNT> actives;
    std::array<QByteArray, LAYOUT_COUNT> layout_states;
    std::array<std::vector<std::string>, USER_DEFINED_LAYERS_PATHS_COUNT> user_defined_layers_paths;
    std::vector<Application> applications;

    PathManager& paths_manager;

    std::vector<std::string> default_configuration_filenames;

   public:
    const PathManager& paths;
};

bool ExactExecutableFromAppBundle(std::string& path);

// Search for all the applications in the list, an remove the application which executable can't be found
std::vector<Application> RemoveMissingApplications(const std::vector<Application>& applications);

// Create a list of default applications, eg vkcube
std::vector<Application> CreateDefaultApplications();

// Update default applications path to use relative path (really useful only on Windows)
std::vector<Application> UpdateDefaultApplications(const std::vector<Application>& applications);

LoaderMessageLevel GetLoaderDebug(const std::string& value);
std::string GetLoaderDebugToken(LoaderMessageLevel level);