// Copyright (c) Wojciech Figat. All rights reserved.

#if USE_EDITOR

#include "Editor.h"
#include "ProjectInfo.h"
#include "Engine/Core/Log.h"
#include "Scripting/ScriptsBuilder.h"
#include "Windows/SplashScreen.h"
#include "Managed/ManagedEditor.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Serialization/FileReadStream.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/MessageBox.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Engine/Engine.h"
#include "Engine/ShadowsOfMordor/Builder.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h"
#if PLATFORM_LINUX
#include "Engine/Tools/TextureTool/TextureTool.h"
#endif

namespace EditorImpl
{
    bool IsOldProjectXmlFormat = false;
    bool HasFocus = false;
    SplashScreen* Splash = nullptr;

    void OnUpdate();
}

ManagedEditor* Editor::Managed = nullptr;
ProjectInfo* Editor::Project = nullptr;
bool Editor::IsPlayMode = false;
bool Editor::IsOldProjectOpened = true;
int32 Editor::LastProjectOpenedEngineBuild = 0;

void Editor::CloseSplashScreen()
{
    SAFE_DELETE(EditorImpl::Splash);
}

bool Editor::CheckProjectUpgrade()
{
    PROFILE_MEM(Editor);
    const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");

    // Load version cache file
    struct VersionCache
    {
        // When changing this ensure that Flax Launcher properly reads the version
        int32 Major = FLAXENGINE_VERSION_MAJOR;
        int32 Minor = FLAXENGINE_VERSION_MINOR;
        int32 Build = FLAXENGINE_VERSION_BUILD;
        int32 RealSize = sizeof(Real); // Rebuild when changing between Large Worlds
    };
    VersionCache lastVersion;
    if (FileSystem::FileExists(versionFilePath))
    {
        auto file = FileReadStream::Open(versionFilePath);
        if (file)
        {
            file->ReadBytes(&lastVersion, sizeof(lastVersion));

            // Invalidate results if data has issues
            if (file->HasError() || lastVersion.Major < 0 || lastVersion.Minor < 0 || lastVersion.Major > 100 || lastVersion.Minor > 1000)
            {
                lastVersion = VersionCache();
                LOG(Warning, "Invalid version cache data");
            }
            else
            {
                LOG(Info, "Last project open version: {0}.{1}.{2}", lastVersion.Major, lastVersion.Minor, lastVersion.Build);
                LastProjectOpenedEngineBuild = lastVersion.Build;
            }

            Delete(file);
        }
    }

    // Check if project is in the old, deprecated layout
    if (EditorImpl::IsOldProjectXmlFormat)
    {
        // [Deprecated: 16.04.2020, expires 16.04.2021]
        LOG(Warning, "The project is in an old format and will be upgraded.");
        const auto result = MessageBox::Show(TEXT("The Flax project is in an old format and will be upgraded. Loading it may modify existing data so older editor version won't open it. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
        if (result == DialogResult::Yes)
        {
            if (BackupProject())
            {
                LOG(Warning, "Backup failed");
                return true;
            }
        }
        else if (result == DialogResult::No)
        {
            // Don't backup, just load
        }
        else
        {
            // Cancel
            return true;
        }

        const String& root = Globals::ProjectFolder;
        const String& name = Project->Name;
        String codeName;
        for (int32 i = 0; i < name.Length(); i++)
        {
            Char c = name[i];
            if (StringUtils::IsAlnum(c) && c != ' ' && c != '.')
                codeName += c;
        }
        const String& sourceFolder = Globals::ProjectSourceFolder;
        const String gameModuleFolder = sourceFolder / codeName;
        const String gameEditorModuleFolder = sourceFolder / codeName + TEXT("Editor");
        const String tempSourceSetup = Globals::ProjectCacheFolder / TEXT("UpgradeSource");
        const String tempSourceSetupGame = tempSourceSetup / codeName;
        const String tempSourceSetupGameEditor = tempSourceSetup / codeName + TEXT("Editor");

        // Remove old project files
        FileSystem::DeleteFile(root / name + TEXT(".sln"));
        FileSystem::DeleteFile(root / name + TEXT(".csproj"));
        FileSystem::DeleteFile(root / name + TEXT(".csproj.user"));
        FileSystem::DeleteFile(root / name + TEXT(".Editor.csproj"));
        FileSystem::DeleteFile(root / name + TEXT(".Editor.csproj.user"));

        // Remove old cache files
        FileSystem::DeleteDirectory(root / TEXT("Cache/Assemblies"));
        FileSystem::DeleteDirectory(root / TEXT("Cache/bin"));
        FileSystem::DeleteDirectory(root / TEXT("Cache/obj"));
        FileSystem::DeleteDirectory(root / TEXT("Cache/Shaders"));

        // Move C# files to new locations
        FileSystem::DeleteDirectory(tempSourceSetup);
        FileSystem::CreateDirectory(tempSourceSetup);
        Array<String> files;
        FileSystem::DirectoryGetFiles(files, sourceFolder);
        bool useEditorModule = false;
        for (auto& file : files)
        {
            String tempSourceFile;
            if (file.Contains(TEXT("/Editor/")))
            {
                useEditorModule = true;
                tempSourceFile = tempSourceSetupGameEditor / StringUtils::GetFileName(file);
            }
            else
            {
                tempSourceFile = tempSourceSetupGame / FileSystem::ConvertAbsolutePathToRelative(sourceFolder, file);
            }
            FileSystem::CreateDirectory(StringUtils::GetDirectoryName(tempSourceFile));
            FileSystem::CopyFile(tempSourceFile, file);
        }
        FileSystem::DeleteDirectory(sourceFolder);
        FileSystem::CopyDirectory(sourceFolder, tempSourceSetup);
        FileSystem::DeleteDirectory(tempSourceSetup);

        // Generate module files
        File::WriteAllText(gameModuleFolder / String::Format(TEXT("{0}.Build.cs"), codeName), String::Format(TEXT(
                               "using Flax.Build;\n"
                               "using Flax.Build.NativeCpp;\n"
                               "\n"
                               "public class {0} : GameModule\n"
                               "{{\n"
                               "    /// <inheritdoc />\n"
                               "    public override void Setup(BuildOptions options)\n"
                               "    {{\n"
                               "        base.Setup(options);\n"
                               "\n"
                               "        // Ignore compilation warnings due to missing code documentation comments\n"
                               "        options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n"
                               "\n"
                               "        // Here you can modify the build options for your game module\n"
                               "        // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n"
                               "        // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
                               "        // To learn more see scripting documentation.\n"
                               "        BuildNativeCode = false;\n"
                               "    }}\n"
                               "}}\n"
                           ), codeName), Encoding::UTF8);
        if (useEditorModule)
        {
            File::WriteAllText(gameEditorModuleFolder / String::Format(TEXT("{0}Editor.Build.cs"), codeName), String::Format(TEXT(
                                   "using Flax.Build;\n"
                                   "using Flax.Build.NativeCpp;\n"
                                   "\n"
                                   "public class {0}Editor : GameEditorModule\n"
                                   "{{\n"
                                   "    /// <inheritdoc />\n"
                                   "    public override void Setup(BuildOptions options)\n"
                                   "    {{\n"
                                   "        base.Setup(options);\n"
                                   "\n"
                                   "        // Reference game source module to access game code types\n"
                                   "        options.PublicDependencies.Add(\"{0}\");\n"
                                   "\n"
                                   "        // Ignore compilation warnings due to missing code documentation comments\n"
                                   "        options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n"
                                   "\n"
                                   "        // Here you can modify the build options for your game editor module\n"
                                   "        // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n"
                                   "        // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
                                   "        // To learn more see scripting documentation.\n"
                                   "        BuildNativeCode = false;\n"
                                   "    }}\n"
                                   "}}\n"
                               ), codeName), Encoding::UTF8);
        }

        // Generate target files
        File::WriteAllText(sourceFolder / String::Format(TEXT("{0}Target.Build.cs"), codeName), String::Format(TEXT(
                               "using Flax.Build;\n"
                               "\n"
                               "public class {0}Target : GameProjectTarget\n"
                               "{{\n"
                               "    /// <inheritdoc />\n"
                               "    public override void Init()\n"
                               "    {{\n"
                               "        base.Init();\n"
                               "\n"
                               "        // Reference the modules for game\n"
                               "        Modules.Add(nameof({0}));\n"
                               "    }}\n"
                               "}}\n"
                           ), codeName), Encoding::UTF8);
        const String editorTargetGameEditorModule = useEditorModule ? String::Format(TEXT("        Modules.Add(\"{0}Editor\");\n"), codeName) : String::Empty;
        File::WriteAllText(sourceFolder / String::Format(TEXT("{0}EditorTarget.Build.cs"), codeName), String::Format(TEXT(
                               "using Flax.Build;\n"
                               "\n"
                               "public class {0}EditorTarget : GameProjectEditorTarget\n"
                               "{{\n"
                               "    /// <inheritdoc />\n"
                               "    public override void Init()\n"
                               "    {{\n"
                               "        base.Init();\n"
                               "\n"
                               "        // Reference the modules for editor\n"
                               "        Modules.Add(nameof({0}));\n"
                               "{1}"
                               "    }}\n"
                               "}}\n"
                           ), codeName, editorTargetGameEditorModule), Encoding::UTF8);

        // Generate new project file
        Project->ProjectPath = root / String::Format(TEXT("{0}.flaxproj"), codeName);
        Project->GameTarget = codeName + TEXT("Target");
        Project->EditorTarget = codeName + TEXT("EditorTarget");
        Project->SaveProject();

        // Remove old project file
        FileSystem::DeleteFile(root / TEXT("Project.xml"));

        LOG(Warning, "Project layout upgraded!");
    }
    // Check if last version was the same
    else if (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor == FLAXENGINE_VERSION_MINOR)
    {
        // Do nothing
        IsOldProjectOpened = false;
    }
    // Check if last version was older
    else if (lastVersion.Major < FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor < FLAXENGINE_VERSION_MINOR))
    {
        LOG(Warning, "The project was last opened with an older editor version");
        const auto result = MessageBox::Show(TEXT("The project was last opened with an older editor version.\nLoading it may modify existing data, which can result in older editor versions being unable to open it.\n\nDo you want to perform a backup before or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
        if (result == DialogResult::Yes)
        {
            if (BackupProject())
            {
                LOG(Warning, "Backup failed");
                return true;
            }
        }
        else if (result == DialogResult::No)
        {
            // Don't backup, just load
        }
        else
        {
            // Cancel
            return true;
        }
    }
    // Check if last version was newer
    else if (lastVersion.Major > FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor > FLAXENGINE_VERSION_MINOR))
    {
        LOG(Warning, "The project was last opened with a newer editor version");
        const auto result = MessageBox::Show(TEXT("The project was last opened with a newer editor version.\nLoading it may fail and corrupt existing data.\n\nDo you want to perform a backup before loading or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
        if (result == DialogResult::Yes)
        {
            if (BackupProject())
            {
                LOG(Warning, "Backup failed");
                return true;
            }
        }
        else if (result == DialogResult::No)
        {
            // Don't backup, just load
        }
        else
        {
            // Cancel
            return true;
        }
    }

    // When changing between major/minor version clear some caches to prevent possible issues
    if (lastVersion.Major != FLAXENGINE_VERSION_MAJOR || lastVersion.Minor != FLAXENGINE_VERSION_MINOR || lastVersion.RealSize != sizeof(Real))
    {
        LOG(Info, "Cleaning cache files from different engine version");
        FileSystem::DeleteDirectory(Globals::ProjectFolder / TEXT("Cache/Cooker"));
        FileSystem::DeleteDirectory(Globals::ProjectFolder / TEXT("Cache/Intermediate"));
    }

    // Upgrade old 0.7 projects
    // [Deprecated: 01.11.2020, expires 01.11.2021]
    if (lastVersion.Major == 0 && lastVersion.Minor == 7 && lastVersion.Build <= 6197)
    {
        Array<String> files;
        FileSystem::DirectoryGetFiles(files, Globals::ProjectSourceFolder, TEXT("*.Gen.cs"));
        for (auto& file : files)
            FileSystem::DeleteFile(file);
    }

    // Update version the cache file
    {
        auto file = FileWriteStream::Open(versionFilePath);
        if (file)
        {
            lastVersion = VersionCache();
            file->WriteBytes(&lastVersion, sizeof(lastVersion));
            Delete(file);
        }
        else
        {
            LOG(Error, "Failed to create version cache file");
        }
    }

    return false;
}

bool Editor::BackupProject()
{
    // Create backup directory
    auto dstPath = Globals::ProjectFolder + TEXT(" - Backup");
    {
        int32 count = 0;
        while (count < 1000 && FileSystem::DirectoryExists(dstPath))
        {
            dstPath = Globals::ProjectFolder + TEXT(" - Backup") + StringUtils::ToString(count++);
        }
    }

    LOG(Info, "Backup project to \"{0}\"", dstPath);

    // Copy everything
    return FileSystem::CopyDirectory(dstPath, Globals::ProjectFolder);
}

int32 Editor::LoadProduct()
{
    PROFILE_MEM(Editor);

    // Flax Editor product
    Globals::ProductName = TEXT("Flax Editor");
    Globals::CompanyName = TEXT("Flax");

#if FLAX_TESTS
    // Flax Tests use auto-generated temporary project
    CommandLine::Options.Project = Globals::TemporaryFolder / TEXT("Project");
    CommandLine::Options.NewProject = true;
#endif

    // Gather project directory from the command line
    String projectPath = CommandLine::Options.Project.TrimTrailing();
    const int32 startIndex = projectPath.StartsWith('\"') || projectPath.StartsWith('\'') ? 1 : 0;
    const int32 length = projectPath.Length() - (projectPath.EndsWith('\"') || projectPath.EndsWith('\'') ? 1 : 0) - startIndex;
    if (length > 0)
    {
        projectPath = projectPath.Substring(startIndex, length - startIndex);
        StringUtils::PathRemoveRelativeParts(projectPath);
        if (FileSystem::IsRelative(projectPath))
        {
            projectPath = Platform::GetWorkingDirectory() / projectPath;
            StringUtils::PathRemoveRelativeParts(projectPath);
        }
        if (projectPath.EndsWith(TEXT(".flaxproj")))
        {
            projectPath = StringUtils::GetDirectoryName(projectPath);
        }
    }
    else
    {
        projectPath.Clear();
    }

    // Create new project option
    if (CommandLine::Options.NewProject.IsTrue())
    {
        Array<String> projectFiles;
        FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly);
        if (projectFiles.Count() > 1)
        {
            Platform::Fatal(TEXT("Too many project files."));
            return -2;
        }
        else if (projectFiles.Count() == 1)
        {
            LOG(Info, "Skip creating new project because it already exists");
            CommandLine::Options.NewProject.Reset();
        }
        else
        {
            Array<String> files;
            FileSystem::DirectoryGetFiles(files, projectPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
            if (files.Count() > 0)
            {
                Platform::Fatal(String::Format(TEXT("Target project folder '{0}' is not empty."), projectPath));
                return -1;
            }
        }
    }
    if (CommandLine::Options.NewProject.IsTrue())
    {
        if (projectPath.IsEmpty())
            projectPath = Platform::GetWorkingDirectory();
        else if (!FileSystem::DirectoryExists(projectPath))
            FileSystem::CreateDirectory(projectPath);
        FileSystem::NormalizePath(projectPath);
        String folderName = StringUtils::GetFileName(projectPath);
        String tmpName;
        for (int32 i = 0; i < folderName.Length(); i++)
        {
            Char c = folderName[i];
            if (StringUtils::IsAlnum(c) && c != ' ' && c != '.')
                tmpName += c;
        }

        // Create project file
        ProjectInfo newProject;
        newProject.Name = MoveTemp(tmpName);
        newProject.ProjectPath = projectPath / newProject.Name + TEXT(".flaxproj");
        newProject.ProjectFolderPath = projectPath;
        newProject.Version = Version(1, 0);
        newProject.Company = TEXT("My Company");
        newProject.MinEngineVersion = FLAXENGINE_VERSION;
        newProject.GameTarget = TEXT("GameTarget");
        newProject.EditorTarget = TEXT("GameEditorTarget");
        auto& flaxRef = newProject.References.AddOne();
        flaxRef.Name = TEXT("$(EnginePath)/Flax.flaxproj");
        flaxRef.Project = nullptr;
        if (newProject.SaveProject())
            return 10;

        // Generate source files
        if (FileSystem::CreateDirectory(projectPath / TEXT("Content")))
            return 11;
        if (FileSystem::CreateDirectory(projectPath / TEXT("Source/Game")))
            return 11;
        bool failed = File::WriteAllText(projectPath / TEXT("Source/GameTarget.Build.cs"),TEXT(
                                             "using Flax.Build;\n"
                                             "\n"
                                             "public class GameTarget : GameProjectTarget\n"
                                             "{\n"
                                             "    /// <inheritdoc />\n"
                                             "    public override void Init()\n"
                                             "    {\n"
                                             "        base.Init();\n"
                                             "\n"
                                             "        // Reference the modules for game\n"
                                             "        Modules.Add(nameof(Game));\n"
                                             "    }\n"
                                             "}\n"), Encoding::UTF8);
        failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
                                         "using Flax.Build;\n"
                                         "\n"
                                         "public class GameEditorTarget : GameProjectEditorTarget\n"
                                         "{\n"
                                         "    /// <inheritdoc />\n"
                                         "    public override void Init()\n"
                                         "    {\n"
                                         "        base.Init();\n"
                                         "\n"
                                         "        // Reference the modules for editor\n"
                                         "        Modules.Add(nameof(Game));\n"
                                         "    }\n"
                                         "}\n"), Encoding::UTF8);
        failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
                                         "using Flax.Build;\n"
                                         "using Flax.Build.NativeCpp;\n"
                                         "\n"
                                         "public class Game : GameModule\n"
                                         "{\n"
                                         "    /// <inheritdoc />\n"
                                         "    public override void Init()\n"
                                         "    {\n"
                                         "        base.Init();\n"
                                         "\n"
                                         "        // C#-only scripting\n"
                                         "        BuildNativeCode = false;\n"
                                         "    }\n"
                                         "\n"
                                         "    /// <inheritdoc />\n"
                                         "    public override void Setup(BuildOptions options)\n"
                                         "    {\n"
                                         "        base.Setup(options);\n"
                                         "\n"
                                         "        options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n"
                                         "\n"
                                         "        // Here you can modify the build options for your game module\n"
                                         "        // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n"
                                         "        // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
                                         "        // To learn more see scripting documentation.\n"
                                         "    }\n"
                                         "}\n"), Encoding::UTF8);
        if (failed)
            return 12;
    }

    // Get the last opened project path
    String localCachePath;
    FileSystem::GetSpecialFolderPath(SpecialFolder::AppData, localCachePath);
    String editorConfigPath = localCachePath / TEXT("Flax");
    String lastProjectSettingPath = editorConfigPath / TEXT("LastProject.txt");
    if (!FileSystem::DirectoryExists(editorConfigPath))
        FileSystem::CreateDirectory(editorConfigPath);
    String lastProjectPath;
    if (FileSystem::FileExists(lastProjectSettingPath))
        File::ReadAllText(lastProjectSettingPath, lastProjectPath);
    if (!FileSystem::DirectoryExists(lastProjectPath))
        lastProjectPath = String::Empty;

    // Try to open the last project when requested
    if (projectPath.IsEmpty() && CommandLine::Options.LastProject.IsTrue() && !lastProjectPath.IsEmpty())
        projectPath = lastProjectPath;

    // Missing project case
    if (projectPath.IsEmpty())
    {
#if PLATFORM_HAS_HEADLESS_MODE
        if (CommandLine::Options.Headless.IsTrue())
        {
            Platform::Fatal(TEXT("Missing project path."));
            return -1;
        }
#endif

        // Ask user to pick a project to open
        Array<String> files;
        if (FileSystem::ShowOpenFileDialog(
            nullptr,
            lastProjectPath,
            TEXT("Project files (*.flaxproj)\0*.flaxproj\0All files (*.*)\0*.*\0"),
            false,
            TEXT("Select project to open in Editor"),
            files) || files.Count() != 1)
        {
            return -1;
        }
        if (!FileSystem::FileExists(files[0]))
        {
            Platform::Fatal(TEXT("Cannot open selected project file because it doesn't exist."));
            return -1;
        }
        projectPath = StringUtils::GetDirectoryName(files[0]);
        StringUtils::PathRemoveRelativeParts(projectPath);
    }

    // Check folder with project exists
    if (!FileSystem::DirectoryExists(projectPath))
    {
        Platform::Fatal(String::Format(TEXT("Project folder '{0}' is missing"), projectPath));
        return -1;
    }

    Globals::ProjectFolder = projectPath;
    ASSERT(!FileSystem::IsRelative(Globals::ProjectFolder));

    // Check if opening old project (the one with Project.xml file)
    // [Deprecated: 16.04.2020, expires 16.04.2021]
    const String projectXmlPath = projectPath / TEXT("Project.xml");
    Project = New<ProjectInfo>();
    ProjectInfo::ProjectsCache.Add(Project);
    if (FileSystem::FileExists(projectXmlPath))
    {
        // Load project
        const bool loadResult = Project->LoadOldProject(projectXmlPath);
        if (loadResult)
        {
            Platform::Fatal(TEXT("Cannot load project."));
            return -2;
        }
        EditorImpl::IsOldProjectXmlFormat = true;
    }
    else
    {
        // Load project
        Array<String> projectFiles;
        FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly);
        if (projectFiles.Count() == 0)
        {
            Platform::Fatal(TEXT("Missing project file (*.flaxproj)."));
            return -2;
        }
        if (projectFiles.Count() > 1)
        {
            Platform::Fatal(TEXT("Too many project files."));
            return -2;
        }
        const bool loadResult = Project->LoadProject(projectFiles[0]);
        if (loadResult)
        {
            Platform::Fatal(TEXT("Cannot load project."));
            return -2;
        }
    }

    HashSet<ProjectInfo*> projects;
    Project->GetAllProjects(projects);

    // Validate project min supported version (older engine may try to load newer project)
    // Special check if project specifies only build number, then major/minor fields are set to 0
    const auto engineVersion = FLAXENGINE_VERSION;
    for (const auto& e : projects)
    {
        const auto project = e.Item;
        if (project->MinEngineVersion > engineVersion ||
            (project->MinEngineVersion.Major() == 0 && project->MinEngineVersion.Minor() == 0 && project->MinEngineVersion.Build() > engineVersion.Build())
        )
        {
            Platform::Fatal(String::Format(TEXT("Cannot open project \"{0}\".\nIt requires version {1} but editor has version {2}.\nPlease update the editor."), project->Name, project->MinEngineVersion.ToString(), engineVersion.ToString()));
            return -2;
        }
    }

    // Update the last opened project path
    if (lastProjectPath.Compare(Project->ProjectFolderPath) != 0)
        File::WriteAllText(lastProjectSettingPath, Project->ProjectFolderPath, Encoding::UTF8);

    return 0;
}

Window* Editor::CreateMainWindow()
{
    PROFILE_MEM(Editor);
    Window* window = Managed->GetMainWindow();

#if PLATFORM_LINUX || PLATFORM_MAC
    // Set window icon
    const String iconPath = Globals::BinariesFolder / TEXT("Logo.png");
    if (FileSystem::FileExists(iconPath))
    {
        TextureData icon;
        if (TextureTool::ImportTexture(iconPath, icon))
        {
            LOG(Warning, "Failed to load icon file.");
        }
        else
        {
            window->SetIcon(icon);
        }
    }
    else
    {
        LOG(Warning, "Missing icon file.");
    }
#endif
    return window;
}

bool Editor::Init()
{
    // Scripts project files generation from command line
    if (CommandLine::Options.GenProjectFiles.IsTrue())
    {
        const String customArgs = TEXT("-verbose -log -logfile=\"Cache/Intermediate/ProjectFileLog.txt\"");
        const bool failed = ScriptsBuilder::GenerateProject(customArgs);
        exit(failed ? 1 : 0);
        return true;
    }
    PROFILE_CPU();
    PROFILE_MEM(Editor);

    // If during last lightmaps baking engine crashed we could try to restore the progress
    ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();

    Engine::Update.Bind(&EditorImpl::OnUpdate);
    Managed = New<ManagedEditor>();

    // Show splash screen
    if (!CommandLine::Options.Headless.IsTrue())
    {
        PROFILE_CPU_NAMED("Splash");
        if (EditorImpl::Splash == nullptr)
            EditorImpl::Splash = New<SplashScreen>();
        EditorImpl::Splash->SetTitle(Project->Name);
        EditorImpl::Splash->Show();
    }

    // Initialize managed editor
    Managed->Init();

    // Start play if requested by cmd line
    if (CommandLine::Options.Play.HasValue())
    {
        Managed->RequestStartPlayOnEditMode();
    }

    return false;
}

void Editor::BeforeRun()
{
    PROFILE_MEM(Editor);
    Managed->BeforeRun();
}

void Editor::BeforeExit()
{
    PROFILE_MEM(Editor);
    CloseSplashScreen();

    Managed->Exit();
    SAFE_DELETE(Managed);
    Project = nullptr;
    ProjectInfo::ProjectsCache.ClearDelete();
}

void EditorImpl::OnUpdate()
{
    PROFILE_MEM(Editor);

    // Update c# editor
    Editor::Managed->Update();

    // If editor thread doesn't have the focus, don't suck up too much CPU time
    const auto hasFocus = Engine::HasFocus;
    if (HasFocus && !hasFocus)
    {
        // Drop our priority to speed up whatever is in the foreground
        Platform::SetThreadPriority(ThreadPriority::BelowNormal);
    }
    else if (hasFocus && !HasFocus)
    {
        // Boost our priority back to normal
        Platform::SetThreadPriority(ThreadPriority::Normal);
    }
    HasFocus = hasFocus;
}

#endif
