/// <summary>
        /// Generates entry point for the applicaton `main.js`
        /// This script will contain the system scheduling, window setup and initial group loading
        /// </summary>
        private static void GenerateMain(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var project  = options.Project;
            var registry = project.Registry;
            var module   = project.Module.Dereference(registry);

            var file = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KMainFileName));

            var writer = new UTinyCodeWriter();

            PrependGeneratedHeader(writer, options.Project.Name);

            var distVersionFile = new FileInfo("UTiny/version.txt");
            var versionString   = "internal";

            if (distVersionFile.Exists)
            {
                versionString = File.ReadAllText(distVersionFile.FullName);
            }
            writer.LineFormat("console.log('runtime version: {0}');", versionString)
            .Line();

            var namespaces = new Dictionary <string, string>();

            foreach (var m in module.EnumerateDependencies())
            {
                if (string.IsNullOrEmpty(m.Namespace))
                {
                    continue;
                }

                if (m.IsRuntimeIncluded)
                {
                    writer.Line($"ut.importModule({m.Namespace});");
                    continue;
                }

                string content;
                namespaces.TryGetValue(m.Namespace, out content);
                content += m.Documentation.Summary;
                namespaces[m.Namespace] = content;
            }

            UTinyJsdoc.WriteType(writer, "ut.World", "Singleton world instance");
            writer.Line("var world;");
            using (writer.Scope("ut.main = function()"))
            {
                // Create and setup the world
                writer
                .Line("world = new ut.World();")
                .Line("var options = WorldSetup(world);");

                // Write configurations
                var context = new EntityGroupSetupVisitor.VisitorContext
                {
                    Project        = project,
                    Module         = project.Module.Dereference(project.Registry),
                    Registry       = project.Registry,
                    Writer         = writer,
                    EntityIndexMap = null
                };

                var configuration = project.Configuration.Dereference(registry);
                foreach (var component in configuration.Components)
                {
                    var moduleContainingType = registry.FindAllByType <UTinyModule>().First(m => m.Types.Contains(component.Type));
                    if (!module.EnumerateDependencies().Contains(moduleContainingType))
                    {
                        // Silently ignore components if the module is not included.
                        // This is by design to preserve user data
                        continue;
                    }

                    var type  = component.Type.Dereference(component.Registry);
                    var index = ++context.ComponentIndex;
                    writer.Line($"var c{index} = world.config({UTinyBuildPipeline.GetJsTypeName(type)});");
                    component.Properties.Visit(new EntityGroupSetupVisitor.ComponentVisitor
                    {
                        VisitorContext = context,
                        Path           = $"c{index}",
                    });
                }

                // Setup the scheduler
                writer.Line("var scheduler = world.scheduler();");

                // Schedule all systems
                var systems = project.Module.Dereference(project.Registry).GetSystemExecutionOrder();
                foreach (var reference in systems)
                {
                    var system = reference.Dereference(project.Registry);

                    if (system == null)
                    {
                        Debug.LogWarning($"Can't resolve system named '{reference.Name}' with ID {reference.Id} -- ignoring, you should delete this system");
                        continue;
                    }

                    var systemModule = UTinyUtility.GetModules(system).FirstOrDefault();
                    var systemName   = UTinyBuildPipeline.GetJsTypeName(systemModule, system);
                    writer.LineFormat("scheduler.schedule({0});", systemName);
                }

                // Enable/disable systems
                foreach (var reference in systems)
                {
                    var system = reference.Dereference(project.Registry);

                    // By default systems are enabled when scheduled, nothing to write
                    if (system == null || system.Enabled)
                    {
                        continue;
                    }

                    var systemModule = UTinyUtility.GetModules(system).FirstOrDefault();
                    var systemName   = UTinyBuildPipeline.GetJsTypeName(systemModule, system);

                    // @NOTE Disable currently accepts a string and NOT the `ut.System` object
                    writer.LineFormat("scheduler.disable({0});", EscapeJsString(systemName));
                }

                writer.Line("try { ut.Runtime.Service.run(world); } catch (e) { if (e !== 'SimulateInfiniteLoop') throw e; }");
            }

            writer.Line();

            using (writer.Scope("function WorldSetup(world)"))
            {
                writer.LineFormat("UT_ASSETS_SETUP(world);");

                var startupEntityGroup = module.StartupEntityGroup.Dereference(module.Registry);

                if (null != startupEntityGroup)
                {
                    writer.Line($"{KEntityGroupNamespace}.{module.Namespace}[\"{module.StartupEntityGroup.Dereference(module.Registry).Name}\"].load(world);");
                }
                else
                {
                    Debug.LogError($"{UTinyConstants.ApplicationName}: BuildError - No startup group has been set");
                }

                using (writer.Scope("return"))
                {
                    writer
                    .LineFormat("canvasWidth: {0},", project.Settings.CanvasWidth)
                    .LineFormat("canvasHeight: {0},", project.Settings.CanvasHeight)
                    .LineFormat("canvasAutoResize: {0},", project.Settings.CanvasAutoResize ? "true" : "false");
                }

#if UNITY_EDITOR_WIN
                writer.Length -= 2;
#else
                writer.Length -= 1;
#endif
                writer.WriteRaw(";").Line();
            }

            File.WriteAllText(file.FullName, writer.ToString(), Encoding.UTF8);
            results.BuildReport.GetOrAddChild(UTinyBuildReport.CodeNode).AddChild(file);
        }
        /// <summary>
        /// Packages system objects to `systems.js`
        ///
        /// All systems and system dependencies are written to this file
        /// </summary>
        private static void GenerateSystems(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var project = options.Project;
            var report  = results.BuildReport.GetOrAddChild(UTinyBuildReport.CodeNode).AddChild();

            var file   = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KSystemsFileName));
            var writer = new UTinyCodeWriter(CodeStyle.JavaScript);

            PrependGeneratedHeader(writer, options.Project.Name);

            foreach (var reference in project.Module.Dereference(project.Registry).GetSystemExecutionOrder())
            {
                var system = reference.Dereference(project.Registry);

                if (system == null)
                {
                    Debug.LogWarning($"Can't resolve system named '{reference.Name}' with ID {reference.Id} -- ignoring, you should delete this system");
                    continue;
                }

                if (system.External)
                {
                    continue;
                }

                // Fetch the module this system belongs to
                var systemModule = UTinyUtility.GetModules(system).FirstOrDefault();

                if (system.IsRuntimeIncluded)
                {
                    continue;
                }

                var reportSystemPos = writer.Length;

                UTinyJsdoc.WriteSystem(writer, system);

                writer.Line($"{UTinyBuildPipeline.GetJsTypeName(systemModule, system)}.update = {UTinyBuildPipeline.GenerateSystemPrefix(system)}");
                writer.IncrementIndent();

                if (system.IncludeIterator)
                {
                    writer.Line(UTinyBuildPipeline.GenerateSystemIteratorPrefix(system));
                    writer.IncrementIndent();
                }

                var text = system.TextAsset ? system.TextAsset.text : string.Empty;

                if (!string.IsNullOrEmpty(text))
                {
                    var lines = text.Split('\n');

                    foreach (var line in lines)
                    {
                        writer.Line(line);
                    }
                }

                if (system.IncludeIterator)
                {
                    writer.DecrementIndent();
                    writer.Line("});");
                }

                writer.DecrementIndent();
                writer.Line(UTinyBuildPipeline.GenerateSystemSuffix(system));

                report.AddChild(AssetDatabase.GetAssetPath(system.TextAsset), Encoding.ASCII.GetBytes(writer.Substring(reportSystemPos)), system.TextAsset);
            }

            File.WriteAllText(file.FullName, writer.ToString(), Encoding.UTF8);
            report.Reset(file);
        }