<commit_before>﻿using SkyEditor.Core.IO;
using SkyEditor.Core.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SkyEditor.Core.Projects
{
    /// <summary>
    /// Defines the common functionality of both projects and solutions
    /// </summary>
    public abstract class ProjectBase : INotifyModified, IReportProgress, IOnDisk, ISavable, IDisposable
    {
        /// <summary>
        /// Creates a new project
        /// </summary>
        /// <typeparam name="T">Type of the project</typeparam>
        /// <param name="parentPath">Directory in which the project directory will be created</param>
        /// <param name="projectName">Name of the project</param>
        /// <param name="projectType">Type of the project</param>
        /// <param name="manager">Instance of the current plugin manager</param>
        /// <returns>The newly created project</returns>
        public static T CreateProject<T>(string parentPath, string projectName, Type projectType, PluginManager manager) where T : ProjectBase
        {
            // Create the instance
            var output = ReflectionHelpers.CreateInstance(projectType) as T;

            // Get the filename
            var filename = Path.Combine(parentPath, projectName, projectName + "." + output.ProjectFileExtension);
            // Create the directory if it doesn't exist
            if (!manager.CurrentIOProvider.DirectoryExists(Path.GetDirectoryName(filename)))
            {
                manager.CurrentIOProvider.CreateDirectory(Path.GetDirectoryName(filename));
            }

            // Set the properties
            output.Filename = filename;
            output.CurrentPluginManager = manager;
            output.Name = projectName;
            output.Settings = new SettingsProvider(manager);
            return output;
        }

        /// <summary>
        /// Opens a project
        /// </summary>
        /// <typeparam name="T">Type of the project to load</typeparam>
        /// <param name="filename">Path of the project file</param>
        /// <param name="manager">Instance of the current plugin manager</param>
        /// <returns>The newly-opened project</returns>
        public static async Task<T> OpenProjectFile<T>(string filename, PluginManager manager) where T : ProjectBase
        {
            // Open the file
            var file = Json.DeserializeFromFile<ProjectFile>(filename, manager.CurrentIOProvider);

            // Get the type
            var projectType = ReflectionHelpers.GetTypeByName(file.AssemblyQualifiedTypeName, manager);
            if (projectType == null)
            {
                // Can't find the type.  Use a dummy one so basic loading can continue.
                projectType = typeof(UnsupportedProject).GetTypeInfo();
            }

            // Create the project & load basic info
            var output = ReflectionHelpers.CreateInstance(projectType) as T;
            output.Filename = filename;
            output.CurrentPluginManager = manager;
            output.Name = file.Name;
            output.Settings = SettingsProvider.Deserialize(file.InternalSettings, manager);

            // Load items
            var itemLoadTasks = new Dictionary<string, Task<IOnDisk>>();
            foreach (var item in file.Items)
            {
                if (item.Value == null)
                {
                    // Directory
                    output.CreateDirectory(item.Key);
                }
                else
                {
                    // Item
                    itemLoadTasks.Add(item.Key, output.LoadProjectItem(item.Value));
                }
            }

            // Add the items
            foreach (var item in itemLoadTasks)
            {
                output.AddItem(item.Key, await item.Value);
            }

            return output;
        }

        #region Child Classes

        protected class ItemValue : IOnDisk
        {
            public string AssemblyQualifiedTypeName { get; set; }
            public string Filename { get; set; }
        }

        protected class ProjectFile
        {
            public ProjectFile()
            {
                Items = new Dictionary<string, ItemValue>();
            }
            public const string CurrentVersion = "v2";
            public string FileFormat { get; set; }
            public string AssemblyQualifiedTypeName { get; set; }
            public string Name { get; set; }
            public Dictionary<string, ItemValue> Items { get; set; }
            public string InternalSettings { get; set; }
        }
        #endregion

        #region Events
        /// <summary>
        /// Raised when the project completes a build
        /// </summary>
        public event EventHandler Completed;

        /// <summary>
        /// Raised when a directory is created
        /// </summary>
        public event EventHandler<DirectoryCreatedEventArgs> DirectoryCreated;

        /// <summary>
        /// Raised when a directory is deleted
        /// </summary>
        public event EventHandler<DirectoryDeletedEventArgs> DirectoryDeleted;

        /// <summary>
        /// Raised when an item is added
        /// </summary>
        public event EventHandler<ItemAddedEventArgs> ItemAdded;

        /// <summary>
        /// Raised when an item is removed
        /// </summary>
        public event EventHandler<ItemRemovedEventArgs> ItemRemoved;

        /// <summary>
        /// Raised when the project is modified
        /// </summary>
        public event EventHandler Modified;

        /// <summary>
        /// Raised when the build progresses
        /// </summary>
        public event EventHandler<ProgressReportedEventArgs> ProgressChanged;

        /// <summary>
        /// Raised when the project file has been saved
        /// </summary>
        public event EventHandler FileSaved;
        #endregion

        #region Properties

        /// <summary>
        /// Instance of the current plugin manager
        /// </summary>
        public PluginManager CurrentPluginManager { get; set; }

        /// <summary>
        /// Path of the project file
        /// </summary>
        public string Filename { get; set; }

        /// <summary>
        /// Name of the project
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The default extension used for the project file
        /// </summary>
        public abstract string ProjectFileExtension { get; }

        /// <summary>
        /// Settings associated with the project
        /// </summary>
        public SettingsProvider Settings { get; set; }

        /// <summary>
        /// Matches logical paths to items
        /// </summary>
        /// <remarks>Key: logical path; Value: Item (Projects for Solutions, Files for Projects).
        /// If the value is null, the path is an empty directory.
        ///
        /// Example Paths (In form: "{Path}"/{Value})
        /// ""/null - Represents the root directory
        /// "/Test"/null - directory
        /// "/Test/Ing"/null - directory
        /// "/Test/File"/[GenericFile] - File of type GenericFile, named "File", in directory "Test"</remarks>
        private Dictionary<string, IOnDisk> Items { get; set; }

        /// <summary>
        /// Whether or not the project has unsaved changes
        /// </summary>
        public bool HasUnsavedChanges
        {
            get
            {
                return _unsavedChanges;
            }
            set
            {
                _unsavedChanges = value;
                if (value)
                {
                    Modified?.Invoke(this, new EventArgs());
                }
            }
        }
        bool _unsavedChanges;

        // Build-related properties

        /// <summary>
        /// The progress of the current build
        /// </summary>
        public float Progress
        {
            get
            {
                return _progress;
            }
            set
            {
                if (_progress != value)
                {
                    _progress = value;
                    ProgressChanged?.Invoke(this, new ProgressReportedEventArgs { IsIndeterminate = IsIndeterminate, Message = Message, Progress = Progress });
                }
            }
        }
        private float _progress;

        /// <summary>
        /// What the current build is doing
        /// </summary>
        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                if (_message != value)
                {
                    _message = value;
                    ProgressChanged?.Invoke(this, new ProgressReportedEventArgs { IsIndeterminate = IsIndeterminate, Message = Message, Progress = Progress });
                }
            }
        }
        private string _message;

        /// <summary>
        /// Whether or not the exact build progress can currently be determined
        /// </summary>
        public bool IsIndeterminate
        {
            get
            {
                return _isIndeterminate;
            }
            set
            {
                if (_isIndeterminate != value)
                {
                    _isIndeterminate = value;
                    ProgressChanged?.Invoke(this, new ProgressReportedEventArgs { IsIndeterminate = IsIndeterminate, Message = Message, Progress = Progress });
                }
            }
        }
        private bool _isIndeterminate;

        /// <summary>
        /// Whether or not the current build is complete
        /// </summary>
        public bool IsCompleted { get; set; }

        /// <summary><commit_msg>Implemented INotifyPropertyChanged in ProjectBase<commit_after>﻿using SkyEditor.Core.IO;
using SkyEditor.Core.Utilities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SkyEditor.Core.Projects
{
    /// <summary>
    /// Defines the common functionality of both projects and solutions
    /// </summary>
    public abstract class ProjectBase : INotifyPropertyChanged, INotifyModified, IReportProgress, IOnDisk, ISavable, IDisposable
    {
        /// <summary>
        /// Creates a new project
        /// </summary>
        /// <typeparam name="T">Type of the project</typeparam>
        /// <param name="parentPath">Directory in which the project directory will be created</param>
        /// <param name="projectName">Name of the project</param>
        /// <param name="projectType">Type of the project</param>
        /// <param name="manager">Instance of the current plugin manager</param>
        /// <returns>The newly created project</returns>
        public static T CreateProject<T>(string parentPath, string projectName, Type projectType, PluginManager manager) where T : ProjectBase
        {
            // Create the instance
            var output = ReflectionHelpers.CreateInstance(projectType) as T;

            // Get the filename
            var filename = Path.Combine(parentPath, projectName, projectName + "." + output.ProjectFileExtension);
            // Create the directory if it doesn't exist
            if (!manager.CurrentIOProvider.DirectoryExists(Path.GetDirectoryName(filename)))
            {
                manager.CurrentIOProvider.CreateDirectory(Path.GetDirectoryName(filename));
            }

            // Set the properties
            output.Filename = filename;
            output.CurrentPluginManager = manager;
            output.Name = projectName;
            output.Settings = new SettingsProvider(manager);
            return output;
        }

        /// <summary>
        /// Opens a project
        /// </summary>
        /// <typeparam name="T">Type of the project to load</typeparam>
        /// <param name="filename">Path of the project file</param>
        /// <param name="manager">Instance of the current plugin manager</param>
        /// <returns>The newly-opened project</returns>
        public static async Task<T> OpenProjectFile<T>(string filename, PluginManager manager) where T : ProjectBase
        {
            // Open the file
            var file = Json.DeserializeFromFile<ProjectFile>(filename, manager.CurrentIOProvider);

            // Get the type
            var projectType = ReflectionHelpers.GetTypeByName(file.AssemblyQualifiedTypeName, manager);
            if (projectType == null)
            {
                // Can't find the type.  Use a dummy one so basic loading can continue.
                projectType = typeof(UnsupportedProject).GetTypeInfo();
            }

            // Create the project & load basic info
            var output = ReflectionHelpers.CreateInstance(projectType) as T;
            output.Filename = filename;
            output.CurrentPluginManager = manager;
            output.Name = file.Name;
            output.Settings = SettingsProvider.Deserialize(file.InternalSettings, manager);

            // Load items
            var itemLoadTasks = new Dictionary<string, Task<IOnDisk>>();
            foreach (var item in file.Items)
            {
                if (item.Value == null)
                {
                    // Directory
                    output.CreateDirectory(item.Key);
                }
                else
                {
                    // Item
                    itemLoadTasks.Add(item.Key, output.LoadProjectItem(item.Value));
                }
            }

            // Add the items
            foreach (var item in itemLoadTasks)
            {
                output.AddItem(item.Key, await item.Value);
            }

            return output;
        }

        #region Child Classes

        protected class ItemValue : IOnDisk
        {
            public string AssemblyQualifiedTypeName { get; set; }
            public string Filename { get; set; }
        }

        protected class ProjectFile
        {
            public ProjectFile()
            {
                Items = new Dictionary<string, ItemValue>();
            }
            public const string CurrentVersion = "v2";
            public string FileFormat { get; set; }
            public string AssemblyQualifiedTypeName { get; set; }
            public string Name { get; set; }
            public Dictionary<string, ItemValue> Items { get; set; }
            public string InternalSettings { get; set; }
        }
        #endregion

        #region Events
        /// <summary>
        /// Raised when the project completes a build
        /// </summary>
        public event EventHandler Completed;

        /// <summary>
        /// Raised when a directory is created
        /// </summary>
        public event EventHandler<DirectoryCreatedEventArgs> DirectoryCreated;

        /// <summary>
        /// Raised when a directory is deleted
        /// </summary>
        public event EventHandler<DirectoryDeletedEventArgs> DirectoryDeleted;

        /// <summary>
        /// Raised when an item is added
        /// </summary>
        public event EventHandler<ItemAddedEventArgs> ItemAdded;

        /// <summary>
        /// Raised when an item is removed
        /// </summary>
        public event EventHandler<ItemRemovedEventArgs> ItemRemoved;

        /// <summary>
        /// Raised when the project is modified
        /// </summary>
        public event EventHandler Modified;

        /// <summary>
        /// Raised when the build progresses
        /// </summary>
        public event EventHandler<ProgressReportedEventArgs> ProgressChanged;

        /// <summary>
        /// Raised when the project file has been saved
        /// </summary>
        public event EventHandler FileSaved;
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Properties

        /// <summary>
        /// Instance of the current plugin manager
        /// </summary>
        public PluginManager CurrentPluginManager { get; set; }

        /// <summary>
        /// Path of the project file
        /// </summary>
        public string Filename { get; set; }

        /// <summary>
        /// Name of the project
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The default extension used for the project file
        /// </summary>
        public abstract string ProjectFileExtension { get; }

        /// <summary>
        /// Settings associated with the project
        /// </summary>
        public SettingsProvider Settings { get; set; }

        /// <summary>
        /// Matches logical paths to items
        /// </summary>
        /// <remarks>Key: logical path; Value: Item (Projects for Solutions, Files for Projects).
        /// If the value is null, the path is an empty directory.
        ///
        /// Example Paths (In form: "{Path}"/{Value})
        /// ""/null - Represents the root directory
        /// "/Test"/null - directory
        /// "/Test/Ing"/null - directory
        /// "/Test/File"/[GenericFile] - File of type GenericFile, named "File", in directory "Test"</remarks>
        private Dictionary<string, IOnDisk> Items { get; set; }

        /// <summary>
        /// Whether or not the project has unsaved changes
        /// </summary>
        public bool HasUnsavedChanges
        {
            get
            {
                return _unsavedChanges;
            }
            set
            {
                _unsavedChanges = value;
                if (value)
                {
                    Modified?.Invoke(this, new EventArgs());
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasUnsavedChanges)));
                }
            }
        }
        bool _unsavedChanges;

        // Build-related properties

        /// <summary>
        /// The progress of the current build
        /// </summary>
        public float Progress
        {
            get
            {
                return _progress;
            }
            set
            {
                if (_progress != value)
                {
                    _progress = value;
                    ProgressChanged?.Invoke(this, new ProgressReportedEventArgs { IsIndeterminate = IsIndeterminate, Message = Message, Progress = Progress });
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress)));
                }
            }
        }
        private float _progress;

        /// <summary>
        /// What the current build is doing
        /// </summary>
        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                if (_message != value)
                {
                    _message = value;
                    ProgressChanged?.Invoke(this, new ProgressReportedEventArgs { IsIndeterminate = IsIndeterminate, Message = Message, Progress = Progress });
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Message)));
                }
            }
        }
        private string _message;

        /// <summary>
        /// Whether or not the exact build progress can currently be determined
        /// </summary>
        public bool IsIndeterminate
        {
            get
            {
                return _isIndeterminate;
            }
            set
            {
                if (_isIndeterminate != value)
                {
                    _isIndeterminate = value;
                    ProgressChanged?.Invoke(this, new ProgressReportedEventArgs { IsIndeterminate = IsIndeterminate, Message = Message, Progress = Progress });
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIndeterminate)));
                }
            }
        }
        private bool _isIndeterminate;

        /// <summary>
        /// Whether or not the current build is complete
        /// </summary>
        public bool IsCompleted
        {
            get
            {
                return _isCompleted;
            }
            set
            {
                if (_isCompleted != value)
                {
                    _isCompleted = value;
                    Completed?.Invoke(this, new EventArgs());
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsCompleted)));
                }
            }
        }
        private bool _isCompleted;

        /// <summary>
{'fraction_non_alphanumeric': 0.09491264378880493, 'fraction_numerical': 9.626028781826057e-05, 'mean_word_length': 1.515496368038741, 'pattern_counts': {'":': 0, '<': 165, '<?xml version=': 0, '>': 165, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7304891', 'n_tokens_mistral': 4894, 'n_tokens_neox': 4688, 'n_words': 1417}