/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
 * http://www.gnu.org/licenses/lgpl-3.0.html
 */

#ifndef CBPROJECT_H
#define CBPROJECT_H

#include <wx/datetime.h>
#include <wx/dynarray.h>
#include <wx/hash.h>
#include <wx/hashmap.h>
#include <wx/treectrl.h>

#include "settings.h"
#include "misctreeitemdata.h"
#include "compiletargetbase.h"
#include "cbplugin.h"
#include "projectbuildtarget.h"
#include "projectmanager.h"

#include <map>
#include <vector>
#include <functional>

// forward decl
class cbProject;
class ProjectBuildTarget;
class ProjectFile;
class FilesGroupsAndMasks;
class TiXmlNode;
class TiXmlElement;

// hashmap for fast searches in cbProject::GetFileByFilename()
WX_DECLARE_STRING_HASH_MAP(ProjectFile*, ProjectFiles);

typedef std::map<wxString, wxArrayString> VirtualBuildTargetsMap;

class DLLIMPORT FileTreeData : public MiscTreeItemData
{
    public:
        /// The kind of tree node
        enum FileTreeDataKind
        {
            ftdkUndefined = 0,
            ftdkProject,
            ftdkFolder,
            ftdkFile,
            ftdkVirtualGroup, // wilcard matching
            ftdkVirtualFolder
        };

        FileTreeData(cbProject* project, FileTreeDataKind kind = ftdkUndefined)
            : m_Index(-1),
            m_Project(project),
            m_file(nullptr),
            m_kind(kind)
        {}

        FileTreeDataKind GetKind() const { return m_kind; }
        cbProject* GetProject() const { return m_Project; }
        int GetFileIndex() const { return m_Index; }
        ProjectFile* GetProjectFile() const { return m_file; }
        const wxString& GetFolder() const { return m_folder; }

        void SetKind(FileTreeDataKind kind){ m_kind = kind; }
        void SetProject(cbProject* project){ m_Project = project; }
        // only valid for file selections
        void SetFileIndex(int index){ m_Index = index; }
        void SetProjectFile(ProjectFile* file){ m_file = file; }
        // only valid for folder selections
        void SetFolder(const wxString& folder){ m_folder = folder; }
    private:
        int m_Index;
        cbProject* m_Project;
        ProjectFile* m_file;
        wxString m_folder;
        FileTreeDataKind m_kind;
};

/** Precompiled headers mode.
  * Defines where and how are the project's precompiled headers generated.
  * Currently implemented only for GCC (3.4 and above).
  */
enum PCHMode
{
    pchSourceDir = 0, /// In a dir (named by the PCH) on the same level as the source header (default).
    pchObjectDir,     /// In the objects output dir, along with other object files.
    pchSourceFile     /// In a file alongside the source header (with .gch appended).
};


/** @brief Represents a project glob.
  *
  * A glob is a combination of path, wildcard and a recursive flag. This describes a
  * special directory from what all files that match the wildcard should be loaded
  * dynamically. This directory should be loaded continuously or at least during
  * load of the project (for non GUI mode). The loading of the files is handled in
  * the ProjectLoader.
  *
  * For each glob it is possible to set targets, the file gets added to when loaded from disk
  * targets are NOT hashed in the id, because when a target changes it is not needed to reload
  * directories watcher, this changes only file properties. It is not possible to have two
  * globs with same path/recursive option and different targets. So the id has to be the same
  *
  * A user can set if files are added to project or not on saving. If the user adds files
  * to the project he can modify compiler flags and targets per file.
  * If he does not add them to the project file, thy can not be saved.
  * Also this is not part of the hash id
  *
  * @see ProjectManagerUI, ProjectLoader
  */
class ProjectGlob
{
    public:

        /** \brief Default constructor
         * Id is generated automatically from path, wildcard and recursive flag
         * \param path Base path for the glob
         * \param wildCard Wildcard for files to search for
         * \param recursive Search all subfolders of the path
         *
         */
        ProjectGlob(const wxString& path, const wxString& wildCard, bool recursive) : m_Path(path), m_WildCard(wildCard), m_Recursive(recursive)
        {
           UpdateId();
        }

        /** \brief Constructor without ID generation. This is intended for file loading.
         * \param id Id of this glob. Has to be generated by this class.
         * \param path Base path for the glob
         * \param wildCard Wildcard for files to search for
         * \param recursive Search all subfolders of the path
         *
         */
        ProjectGlob(GlobId id, const wxString& path, const wxString& wildCard, bool recursive) : m_Id(id), m_Path(path), m_WildCard(wildCard), m_Recursive(recursive)
        {
        }

         /** \brief Default empty consturctor
         */
        ProjectGlob()
        {
            m_Id = InvalidGlobId;
        }

        bool operator==(const ProjectGlob& other) const
        {
            if (other.GetId() != this->GetId() )
                return false;
            // If the ID is the same, we make a deep comparison, to evade collisions...
            return this->m_WildCard == other.m_WildCard &&
                   this->m_Recursive == other.m_Recursive &&
                   this->m_Path == other.m_Path &&
                   this->m_Targets == other.m_Targets &&
                   this->m_AddToProject == other.m_AddToProject;
        }

        /** \brief Check if this glob is valid
         * \return true if all parameter are valid for this glob
         */
        bool IsValid()           const { return !m_Path.IsEmpty() && m_Id != InvalidGlobId; }

        GlobId GetId()             const { return m_Id; }
        wxString GetIdAsString() const { wxString ret; ret << m_Id; return ret; }
        wxString GetPath()       const { return m_Path; }
        wxString GetWildCard()   const { return m_WildCard; }
        bool GetRecursive()      const { return m_Recursive; }
        bool GetAddToProject()   const { return m_AddToProject; }
        wxArrayString GetTargets() const { return m_Targets; }
        void SetTargets(const wxArrayString& targets) { m_Targets = targets; }

        void SetAddToProject(bool addToProject) { m_AddToProject = addToProject; }


        void Set(const wxString& path, const wxString& wildCard, bool rec)
        {
            m_Path = path;
            m_WildCard = wildCard;
            m_Recursive = rec;
            UpdateId();
        }

        void SetPath(const wxString& path)
        {
            m_Path = path;
            UpdateId();
        }

        void SetWildCard(const wxString& wildCard)
        {
            m_WildCard = wildCard;
            UpdateId();
        }

        void SetRecursive(bool rec)
        {
            m_Recursive = rec;
            UpdateId();
        }

        /** \brief fnv1a hash function (http://www.isthe.com/chongo/tech/comp/fnv/index.html)
         * We use the 32 bit version on a 64 bit integer, so we are sure that we can use -1 for an
         * invalid id
         *
         * \param input const wxString& Input string
         * \return int64_t the hash guaranteed to be positive
         *
         */
        static int64_t fnv1a(const wxString& input)
        {
            uint32_t ret = 2166136261;
            const wxStringCharType *p = input.wx_str();
            while ( *p )
            {
                ret ^= *p++;
                ret *= 16777619;
            }
            return (int64_t) ret;  // Make sure we are always positive
        }

        bool operator<(const ProjectGlob& rhs) const
        {
            return (int64_t) this->GetId() < (int64_t) rhs.GetId();
        }

    private:

        /** \brief Calculate the id from all variables
         *
         */
        void UpdateId()
        {
             m_Id = (GlobId) fnv1a( m_Path.ToStdString() + m_WildCard.ToStdString() + (m_Recursive ? "1" : "0"));
        }

        GlobId m_Id;

        wxString m_Path;
        wxString m_WildCard;
        bool m_Recursive;
        bool m_AddToProject;
        wxArrayString m_Targets;

};



/** @brief Represents a Code::Blocks project.
  *
  * A project is a collection of build targets and files.
  * Each project can contain any number of build targets and files.
  * @see ProjectBuildTarget, ProjectFile.
  */
class DLLIMPORT cbProject : public CompileTargetBase
{
     public:
        /// Constructor
        cbProject(const wxString& filename = wxEmptyString);
        /// Destructor
        ~cbProject() override;

        cbProject& operator=(const cbProject &p);

        /** @return True if the project fully loaded, false if not. */
        bool IsLoaded() const { return m_Loaded; }

        /** This resets the project to a clear state. Like it's just been new'ed. */
        void ClearAllProperties();

        /** Calculates the top-level path common to all project files.
          * This is called automatically (no need for you to call it) and is used
          * to find the top-level folder for building the tree.
          */
        void CalculateCommonTopLevelPath();

        /** @return the top-level path common to all project files. */
        wxString GetCommonTopLevelPath() const;

        /** @return True if the project is modified in any way. */
        bool GetModified() const override;

        /** Mark the project as modified or not.
          * @param modified If true, the project is marked as modified. If false, as not-modified.
          */
        void SetModified(bool modified = true) override;

        /** Access a file of the project.
          * @param index The index of the file. Must be greater or equal than zero and less than GetFilesCount().
          * @return A pointer to the file or NULL if not found.
          */
        ProjectFile* GetFile(int index);

        /** Access a file of the project.
          * @param filename The filename of the file.
          * @param isRelative True if @c filename is a relative filename, false if not.
          * @param isUnixFilename True if @c filename is already normalized with UnixFilename(), false if not.
          * @return A pointer to the file or NULL if not found.
          */
        ProjectFile* GetFileByFilename(const wxString& filename, bool isRelative = true, bool isUnixFilename = false);

        /** @return The number of files in the project. */
        int GetFilesCount() const { return m_Files.size(); }

        /** Set the Makefile filename used when exporting a Makefile for the project,
          * or when using a custom Makefile to build the project.
          * @param makefile The filename for the Makefile.
          */
        void SetMakefile(const wxString& makefile){ m_Makefile = makefile; SetModified(true); }

        /** @return The filename for the Makefile. */
        const wxString& GetMakefile() const;

        /** Mark if the project should use a custom Makefile for compilation.
          * @param custom If true, use a custom Makefile for compilation. If false, use direct C::B build mode.
          */
        void SetMakefileCustom(bool custom);

        /** @return True if the project is using a custom Makefile for compilation, false if not. */
        bool IsMakefileCustom() const { return m_CustomMakefile; }

        /** Allow the specification of specific execution directory if the project use a custom Makefile.
          * Defaults to the projects base path, if no custom makefile is used.
          * @param dir The directory the custom Makefile should be executed from.
          */
        void SetMakefileExecutionDir(const wxString& dir);

        /** @return The execution directory for the custom Makefile.
          * Defaults to the projects base path, if no custom makefile is used.
          */
        wxString GetMakefileExecutionDir();

        /** @return Either the execution directory for the custom Makefile or
          * the projects base path.
          * Defaults to the projects base path, if no custom makefile is used.
          * Used to unify the call to the function.
          */
        wxString GetExecutionDir();

        /** Is there a build target (virtual or real) by @c name?
          * @param name The build target's name.
          * @param virtuals_too Include virtual build targets in query.
          * @return True if exists a build target (virtual or real) by that name, false if not.
          */
        bool BuildTargetValid(const wxString& name, bool virtuals_too = true) const;

        /** @return The first valid (virtual or real) build target. */
        wxString GetFirstValidBuildTargetName(bool virtuals_too = true) const;

        /** @return The build target index which will be pre-selected when the "Select target"
          * dialog appears when running the project. Valid only for multi-target projects. */
        const wxString& GetDefaultExecuteTarget() const;

        /** Set the build target index which will be pre-selected when the "Select target"
          * dialog appears when running the project.
          * @param name The build target's name.
          */
        void SetDefaultExecuteTarget(const wxString& name);

        /** @return The number of build targets this project contains. */
        int GetBuildTargetsCount() const { return m_Targets.GetCount(); }

        /** Access a build target.
          * @param index The build target index. Must be greater or equal to zero and less than GetBuildTargetsCount().
          * @return The build target or NULL if not found.
          */
        ProjectBuildTarget* GetBuildTarget(int index);

        /** Access a build target.
          * @param index The build target index. Must be greater or equal to zero and less than GetBuildTargetsCount().
          * @return The build target or NULL if not found.
          */
        const ProjectBuildTarget* GetBuildTarget(int index) const;

        /** Access a build target.
          * @param targetName The build target name.
          * @return The build target or NULL if not found.
          */
        ProjectBuildTarget* GetBuildTarget(const wxString& targetName);

        /** Access a build target.
          * @param targetName The build target name.
          * @return The build target or NULL if not found.
          */
        const ProjectBuildTarget* GetBuildTarget(const wxString& targetName) const;

        /** Add a new build target.
          * @param targetName The build target name.
          * @return The build target that was just created.
          */
        ProjectBuildTarget* AddBuildTarget(const wxString& targetName);

        /** Rename a build target.
          * @param index The build target's index to rename.
          * @param targetName The new name for the build target.
          * @return True if @c index was valid, false if not.
          */
        bool RenameBuildTarget(int index, const wxString& targetName);

        /** Rename a build target.
          * @param oldTargetName The build target's old name.
          * @param newTargetName The new name for the build target.
          * @return True if @c oldTargetName was valid, false if not.
          */
        bool RenameBuildTarget(const wxString& oldTargetName, const wxString& newTargetName);

        /** Duplicate a build target.
          * @param index The index of the build target to duplicate.
          * @param newName The name for the new build target. If empty, it will be named like "Copy of <base_target_name>".
          * @return The new build target if @c index was valid, NULL if not (or something went wrong).
          */
        ProjectBuildTarget* DuplicateBuildTarget(int index, const wxString& newName = wxEmptyString);

        /** Duplicate a build target.
          * @param targetName The name of the build target to duplicate.
          * @param newName The name for the new build target. If empty, it will be named like "Copy of <base_target_name>".
          * @return The new build target if @c index was valid, NULL if not (or something went wrong).
          */
        ProjectBuildTarget* DuplicateBuildTarget(const wxString& targetName, const wxString& newName = wxEmptyString);

        /** Export a target as a new project.
          * In other words, save a copy of the project containing only the specified target.
          * The user will be prompted with a dialog to select the new project name.
          * @param index The index of the build target to export.
          * @return True on success, false on failure (or dialog cancellation).
          * @note The dialog to select the new project name is not a file dialog because
          * the user is not allowed to save anywhere. The new project must remain "operational"
          * (assuming the target to export, was "operational") so the new project must be saved
          * in the same directory as the original to preserve relative paths.
          */
        bool ExportTargetAsProject(int index);

        /** Export a target as a new project.
          * In other words, save a copy of the project containing only the specified target.
          * The user will be prompted with a dialog to select the new project name.
          * @param targetName The name of the build target to export.
          * @return True on success, false on failure (or dialog cancellation).
          * @see ExportTargetAsProject(int).
          */
        bool ExportTargetAsProject(const wxString& targetName);

        /** Remove a build target.
          * @param index The index of the build target to remove.
          * @return True if @c index was valid, false if not.
          */
        bool RemoveBuildTarget(int index);

        /** Remove a build target.
          * @param targetName The build target name.
          * @return True if the @c targetName was valid, false if not.
          */
        bool RemoveBuildTarget(const wxString& targetName);

        /** Reorder the list of build targets.
          * This is useful, so that when the special build target "All" is being built
          * targets are built in the desired order.
          * @param nameOrder An array of strings containing build target names in the order you desire.
          * The number of array elements must be equal to GetBuildTargetsCount().
          */
        void ReOrderTargets(const wxArrayString& nameOrder);

        /** Set the active build target.
          * @param name The build target name to set as active. If @c name does
          *             not exist, then the first virtual target is set
          *             or the first real target, depending which is valid.
          * @return True if @c name was valid, false if not.
          */
        bool SetActiveBuildTarget(const wxString& name);

        /** @return The active build target name. Note that this might be a virtual target. */
        const wxString& GetActiveBuildTarget() const;

        /** @return The mode precompiled headers are handled. */
        PCHMode GetModeForPCH() const { return m_PCHMode; }

        /** Set the mode to handle precompiled headers.
          * @param mode The desired PCH mode.
          */
        void SetModeForPCH(PCHMode mode){ m_PCHMode = mode; SetModified(true); }

        void SetCompilerID(const wxString& id) override; // overriden

        /** @return The root item of this project in the project manager's tree. */
        wxTreeItemId GetProjectNode(){ return m_ProjectNode; }

        /** Sets the root item of this item, should not be called by user's code! */
        void SetProjectNode(wxTreeItemId node) { m_ProjectNode = node; }

        /** Act like closing all project files, but don't do it.
          * Used to check if any of the project files need saving.
          * @return True if even one project file needs to be saved, false if not.
          */
        bool QueryCloseAllFiles();

        /** Close all project files.
          * @param dontsave If true, no project file will be saved even if modified.
          * If false, any modified file will be saved (default).
          * @return True if successful, false otherwise.
          */
        bool CloseAllFiles(bool dontsave=false);

        /** Save all project files.
          * @return True if successful, false otherwise.
          */
        bool SaveAllFiles();

        /** Save the project.
          * @return True if successful, false otherwise.
          */
        bool Save();

        /** Save the project under a different name.
          * A dialog pops up for the user to choose a new filename for the project.
          * @return True if successful, false otherwise.
          */
        bool SaveAs();

        /** Save the project's layout.
          * Layout is the list of open project files, which one is active,
          * where the cursor is located on each one of those, etc.
          * @return True if successful, false otherwise.
          */
        bool SaveLayout();

        /** Load the project's layout.
          * @see SaveLayout() for info.
          * @return True if successful, false otherwise.
          */
        bool LoadLayout();

        /** Notify that file(s) will be added shortly.
          * This function should be called before calling AddFile().
          * When done calling AddFile() as many times as needed, call
          * EndAddFiles().
          *
          * This sequence of function calls ensures proper events dispatching.
          * This function broadcasts the cbEVT_PROJECT_BEGIN_ADD_FILES event.
          */
        void BeginAddFiles();

        /** Notify that file(s) addition finished.
          * This function should be called when done calling AddFile() as many times as needed.
          *
          * This sequence of function calls ensures proper events dispatching.
          * This function broadcasts the cbEVT_PROJECT_END_ADD_FILES event.
          * @see BeginAddFiles().
          */
        void EndAddFiles();

        /** Notify that file(s) will be removed shortly.
          * This function should be called before calling RemoveFile().
          * When done calling RemoveFile() as many times as needed, call
          * EndRemoveFiles().
          *
          * This sequence of function calls ensures proper events dispatching.
          * This function broadcasts the cbEVT_PROJECT_BEGIN_REMOVE_FILES event.
          */
        void BeginRemoveFiles();

        /** Notify that file(s) removal finished.
          * This function should be called when done calling RemoveFile() as many times as needed.
          *
          * This sequence of function calls ensures proper events dispatching.
          * This function broadcasts the cbEVT_PROJECT_END_REMOVE_FILES event.
          * @see BeginRemoveFiles().
          */
        void EndRemoveFiles();

        /** Add a file to the project.
          * This variation, takes a target name as first parameter.
          * @param targetName The name of the build target to add this file to.
          * @param filename The file's filename. This *must* be a filename relative to the project's path.
          * @param compile If true this file is compiled when building the project.
          * @param link If true this file is linked when building the project.
          * @param weight A value between zero and 100 (defaults to 50). Smaller weight, makes the file compile earlier than those with larger weight.
          * @return The newly added file or NULL if something went wrong.
          */
        ProjectFile* AddFile(const wxString& targetName, const wxString& filename, bool compile = true, bool link = true, unsigned short int weight = 50);

        /** Add a file to the project.
          * This variation, takes a target index as first parameter.
          * @param targetIndex The index of the build target to add this file to.
          * @param filename The file's filename. This *must* be a filename relative to the project's path.
          * @param compile If true this file is compiled when building the project.
          * @param link If true this file is linked when building the project.
          * @param weight A value between zero and 100 (defaults to 50). Smaller weight, makes the file compile earlier than those with larger weight.
          * @return The newly added file or NULL if something went wrong.
          */
        ProjectFile* AddFile(int targetIndex, const wxString& filename, bool compile = true, bool link = true, unsigned short int weight = 50);

        /** Remove a file from the project.
          * @param pf The pointer to ProjectFile.
          * @return True if @c pf was a valid project file, false if not.
          */
        bool RemoveFile(ProjectFile* pf);

        /** Remove a glob from the project
          * @param globs the globs to remove from project.
          */
        void RemoveGlob(const ProjectGlob& glob);
        void RemoveGlobs(const std::vector<ProjectGlob>& globs);

        /** Set the globs to the project. These are directories that are scanned automatically and
          * the files found are added or removed from the file. The path can be searched
          * recursively.
          * This function checks the difference from the current set globs and removes the files
          * automatically if the glob is not present in the new globs vector.
          * The Project is marked as modified
          * @param globs the globs to set at the project
          */
        void SetGlobs(std::vector<ProjectGlob>& globs);

        /** Add a new glob to the project.
          * @param glob the glob to add to the project.
          */
        void AddGlob(const ProjectGlob& glob);

        /** Retrieve the current globs from the project
          */
        const std::vector<ProjectGlob> GetGlobs() const;

        /** Check if the glob exists and return it
          * @return if found a valid ProjectGlob (Check with ProjectGlob::IsValid())
          */
        ProjectGlob SearchGlob(const wxString& path, const wxString& wildCard, bool recursive) const;

        /** Check if the glob exists and return it
          * @return if found a valid ProjectGlob (Check with ProjectGlob::IsValid())
          */
        ProjectGlob SearchGlob(const GlobId& id) const;

        /** Check if the glob exists and return it
          * @return if found a valid ProjectGlob (Check with ProjectGlob::IsValid())
          */
        ProjectGlob SearchGlob(const wxString& id) const;

        /** Convenience function for remembering the project's tree state when refreshing it.
          * @return An array of strings containing the tree-path names of expanded nodes.
          */
        const wxArrayString& ExpandedNodes() { return m_ExpandedNodes; }

        /** Convenience function for remembering the project's tree state when refreshing it.
          * Adds an expanded node in this internal list.
          * @param path The tree-path to add.
          */
        void AddExpandedNode(const wxString& path) { m_ExpandedNodes.Add(path); }

        /** Convenience function for remembering the project's tree state when refreshing it.
          * @param tree The tree control to save its expanded state.
          */
        void SaveTreeState(wxTreeCtrl* tree);

        /** Convenience function for remembering the project's tree state when refreshing it.
          * @param tree The tree control to restore its expanded state to a previously saved.
          */
        void RestoreTreeState(wxTreeCtrl* tree);

        /** Displays a target selection dialog.
          * When invoked, a selection dialog is presented to the user so that he/she
          * can select one target from the list of this project's targets.
          * @param initial The index of the pre-selected target when the dialog is displayed.
          * Defaults to none (-1).
          * @param evenIfOne If true, the dialog is still shown even if the project contains only one target.
          * The default behaviour is to not show the dialog if the project has only one target.
          * @return The target's index that the user selected or -1 if the dialog was cancelled.
          */
        int SelectTarget(int initial = -1, bool evenIfOne = false);

        /** Get a pointer to the currently compiling target.
          * @return While the project is being built, this function returns the currently building
          * target. For all other times, NULL is returned.
          */
        ProjectBuildTarget* GetCurrentlyCompilingTarget() { return m_CurrentlyCompilingTarget; }

        /** Get a pointer to the currently compiling target.
          * @return While the project is being built, this function returns the currently building
          * target. For all other times, NULL is returned.
          */
        const ProjectBuildTarget* GetCurrentlyCompilingTarget() const
        {
            return m_CurrentlyCompilingTarget;
        }

        /** Set the currently compiling target.
          * @note This function is for internal use by compilers only.
          * Using this function in any other place results in undefined behaviour!
          * @param bt The build target that is currently building.
          */
        void SetCurrentlyCompilingTarget(ProjectBuildTarget* bt);

        /** Define a new virtual build target.
          *
          * A virtual build target is not really a build target itself but it is an alias
          * for a group of other build targets, real or virtual.
          * An example is the "All" virtual build target which means "all build targets".
          *
          * @param alias The virtual build target's name.
          * @param targets A list of build target names to include in this virtual build target.
          *                They can be real or other virtual build targets.
          * @return True for success, false for failure. The only reason for this function
          *         to return false is if a *real* build target exists with the same name as @c alias
          *         (or if you pass an empty @c targets array).
          * @note Every time you call this function with the same @c alias parameter, the virtual
          *       build target is re-defined. In other words, it's not an error if @c alias is already
          *       defined.
          */
        bool DefineVirtualBuildTarget(const wxString& alias, const wxArrayString& targets);

        /** Does a virtual build target exist?
          *
          * @param alias The virtual build target's name.
          * @return True if the virtual build target exists, false if not.
          */
        bool HasVirtualBuildTarget(const wxString& alias) const;

        /** Remove a virtual build target.
          *
          * @param alias The virtual build target's name.
          * @return True if the virtual build target was removed, false if not.
          */
        bool RemoveVirtualBuildTarget(const wxString& alias);

        /** Get a list of all defined virtual build targets.
          *
          * @return A list of all defined virtual build targets.
          */
        wxArrayString GetVirtualBuildTargets() const;

        /** Access a virtual build target's group of build targets.
          *
          * @param alias The virtual build target's name.
          * @return The list of all build targets under the alias @c alias.
          */
        const wxArrayString& GetVirtualBuildTargetGroup(const wxString& alias) const;

        /** Access a virtual build target's expanded group of build targets.
          *
          * The difference from GetVirtualBuildTargetGroup() lies in that this function
          * returns the full list of real build targets in this group (by recursively
          * expanding virtual build targets in the group).
          * @param alias The virtual build target's name.
          * @return The expanded list of all real build targets under the alias @c alias.
          */
        wxArrayString GetExpandedVirtualBuildTargetGroup(const wxString& alias) const;

        /** Checks if a build target (virtual or real) can be added to a virtual build target,
          * without causing a circular-reference.
          *
          * @param alias The "parent" virtual build target to add the build target in.
          * @param target The build target to add in the @c alias virtual build target.
          * @return True if a circular reference is not detected, false if it is.
          */
        bool CanAddToVirtualBuildTarget(const wxString& alias, const wxString& target);

        /** Get a list of the virtual folders. Normally used by the project loader only.*/
        const wxArrayString& GetVirtualFolders() const;

        /** Set the virtual folders list. Normally used by the project loader only. */
        void SetVirtualFolders(const wxArrayString& folders);

        /** Appends a new virtual folder to the end of the list only if it doesn't exists.
          * @return True if folder has been appended, false if not. */
        bool AppendUniqueVirtualFolder(const wxString &folder);

        /** Remove all virtual folders starting with folder. */
        void RemoveVirtualFolders(const wxString &folder);

        /** Replaced the oldFolder with newFolder or appends newFolder to the end of the list
          * if oldFolder is not found.*/
        void ReplaceVirtualFolder(const wxString &oldFolder, const wxString &newFolder);

        /** Returns the last modification time for the file. Used to detect modifications outside the Program. */
        wxDateTime GetLastModificationTime() const { return m_LastModified; }

        /** Sets the last modification time for the project to 'now'. Used to detect modifications outside the Program. */
        void Touch();

        /** Sets object names generation to extended/normal mode.
          *
          * In normal mode (the default), the file @c foo.cpp generates the @c foo.o object file.
          * In extended mode, the file @c foo.cpp generates the @c foo.cpp.o object file.
          *
          * This option is useful for large projects containing similarly named files
          * (in the same directory) differing only on their extensions. Using the
          * extended mode with said projects guarantees that each object name will
          * be unique.
          *
          * @param ext Set to true to switch to extended mode, false for normal mode.
          */
        void SetExtendedObjectNamesGeneration(bool ext);

        /** Gets object names generation mode (extended/normal).
          * @return True for extended mode, false for normal mode.
          * @see SetExtendedObjectNamesGeneration.
          */
        bool GetExtendedObjectNamesGeneration() const;

        /** Set notes on the project.
          *
          * @param notes Simple text notes about the project.
          */
        void SetNotes(const wxString& notes);

        /** Get notes on the project.
          *
          * @return Simple text notes about the project.
          */
        const wxString& GetNotes() const;

        /** Set show project notes on load automatically.
          *
          * @param show If true show project notes on load.
          */
        void SetShowNotesOnLoad(bool show);

        /** Get show project notes on load automatically.
          *
          * @return True if show project notes on load is set, false if not.
          */
        bool GetShowNotesOnLoad() const;

        /** Set check for externally modified files.
          *
          * @param True if check for externally modified files is set, false if not.
          */
        void SetCheckForExternallyModifiedFiles(bool check);

        /** Get check for externally modified files.
          *
          * @return True if check for externally modified files is set, false if not.
          */
        bool GetCheckForExternallyModifiedFiles() const;

        /** Show project notes now.
          *
          * @param nonEmptyOnly If true, show notes only if non-empty.
          * @param editable If true, the notes will be editable.
          *
          * @note If @c editable is true, the @c nonEmptyOnly parameter is ignored...
          */
        void ShowNotes(bool nonEmptyOnly, bool editable = false);

        /** Changes project title
          *
          * This function overrides CompileTargetBase::SetTitle.
          * It sends additional notification event to plugins
          * and than calls base function.
          */
        void SetTitle(const wxString& title) override;

        /** Access the \<Extensions\> XML node of this project
          *
          * This function is for advanced users only. Use at your own risk
          * (and respect other plugins authors work under this node).
          *
          * @return The \<Extensions\> XML node.
          * @note This function will never return NULL.
          */
        virtual TiXmlNode* GetExtensionsNode();

        /** Access the \<Extensions\> XML node of this project
          *
          * This function is for advanced users only. Use at your own risk
          * (and respect other plugins authors work under this node).
          *
          * @return The \<Extensions\> XML node.
          * @note This function will return NULL if the object is not created.
          */
        virtual const TiXmlNode* GetExtensionsNode() const;

        /** Convenience function (mainly for scripts) to add nodes/attributes
          * under the \<Extensions\> node.
          *
          * It is mainly useful for scripts that can't otherwise access the XML node.
          * For C++ code, using GetExtensionsNode() is recommended instead (which is much faster).
          *
          * @param stringDesc A string representation of the nodes/attributes to add/edit
          * under \<Extensions\>.
          * @c stringDesc is a string of the form:
          * "node/subnode/.../+subnode:attr=val"
          *
          * The ":attr=val" part is optional.
          * The node/subnode structure will be created if not there.
          * To set more than one attribute, call this function more than once,
          * using the same node/subnode structure.
          * If a node begins with the "plus" sign (+), then this forces adding a new node
          * instead of re-using the existing one (if any).
          */
        virtual void AddToExtensions(const wxString& stringDesc);

        /** Internal use only.
          * Updates the internal hashmap of project files.
          */
        virtual void ProjectFileRenamed(ProjectFile* pf);

        /** Provides an easy way to iterate all the files belonging in this target.
          * @return A list of files belonging in this target.
          */
        virtual FilesList& GetFilesList() { return m_Files; }

        /** Provides an easy way to iterate all the files belonging in this target.
          * @return A list of files belonging in this target.
          */
        virtual const FilesList& GetFilesList() const { return m_Files; }

        /** Some cbProjects need to turn off its notification events. Eg., Clangd_client
            needs to avoid confusing plugins with its management of files not contained by a project.
          */
        virtual void SetNotifications(bool onOrOff) { m_Notifications = onOrOff; }

    private:
        void Open();
        void ExpandVirtualBuildTargetGroup(const wxString& alias, wxArrayString& result) const;
        ProjectBuildTarget* AddDefaultBuildTarget();
        int IndexOfBuildTargetName(const wxString& targetName) const;
        wxString CreateUniqueFilename();
        void NotifyPlugins(wxEventType type, const wxString& targetName = wxEmptyString, const wxString& oldTargetName = wxEmptyString);

        // properties
        VirtualBuildTargetsMap m_VirtualTargets;
        BuildTargets           m_Targets;
        wxString               m_ActiveTarget;
        wxString               m_LastSavedActiveTarget;
        wxString               m_DefaultExecuteTarget;
        mutable wxString       m_Makefile;
        bool                   m_CustomMakefile;
        mutable wxString       m_MakefileExecutionDir;

        std::vector<ProjectGlob> m_Globs;
        FilesList         m_Files;
        ProjectFileArray  m_FileArray;
        wxArrayString     m_ExpandedNodes;
        wxArrayString     m_SelectedNodes;
        bool              m_Loaded;
        wxTreeItemId      m_ProjectNode;

        wxArrayString m_VirtualFolders; // not saved, just used throughout cbProject's lifetime

        bool     m_CurrentlyLoading;
        wxString m_CommonTopLevelPath;
        wxString m_BasePath;

        PCHMode m_PCHMode;

        // hashmap for fast searches in cbProject::GetFileByFilename()
        ProjectFiles        m_ProjectFilesMap; // keeps UnixFilename(ProjectFile::relativeFilename)
        ProjectBuildTarget* m_CurrentlyCompilingTarget;

        wxDateTime m_LastModified;

        bool     m_ExtendedObjectNamesGeneration;
        wxString m_Notes;
        bool     m_AutoShowNotesOnLoad;
        bool     m_CheckForExternallyModifiedFiles;

        // copy of <Extensions> element, in case certain plugins are disabled
        // so that the contents are not lost
        TiXmlElement* m_pExtensionsElement;

        // Turn event notifications for this cbProject on or off (default on)
        bool     m_Notifications;
};

/// Returns a string valid to be used as LD_LIBRARY_PATH (or equivalent).
DLLIMPORT wxString cbGetDynamicLinkerPathForTarget(cbProject *project, ProjectBuildTarget* target);
/// Merges to LD_LIBRARY_PATH/PATH strings together to form a new valid string.
DLLIMPORT wxString cbMergeLibPaths(const wxString &oldPath, const wxString &newPath);

#endif // CBPROJECT_H
