public string Generate(Topic topic)
        {
            if (topic.IsSplashPage())
            {
                return _transformer.Transform(topic, new FileSystem().ReadStringFromFile(topic.File));
            }

            try
            {
                return generate(topic);
            }
            catch (Exception e)
            {
                ConsoleWriter.Write(ConsoleColor.Yellow, "Failed to transform topic at " + topic.File);
                ConsoleWriter.Write(ConsoleColor.Red, e.ToString());

                var document = new HtmlDocument
                {
                    Title = "Error!"
                };

                document.Add("h1").Text("Error!");

                document.Add("pre").Text(e.ToString());


                return document.ToString();
            }
        }
        public static void WriteCSS(HtmlDocument document)
        {
            var css = readFile("Storyteller.bootstrap.min.css") + "\n\n" + readFile("StoryTeller.storyteller.css");
            css += "\n\n" + readFile("StoryTeller.fixed-data-table.min.css");

            document.Head.Add("style").Text(css).Encoded(false);
        }
        private static void writeEnbeddedJavascript(HtmlDocument document)
        {
            var stream = typeof(ExportWriter).GetTypeInfo().Assembly.GetManifestResourceStream("StorytellerRunner.preview.js");
            var reader = new StreamReader(stream);
            var js = reader.ReadToEnd();

            document.Body.Add("script").Attr("language", "javascript").Text("\n\n" + js + "\n\n").Encoded(false);
        }
        public static HtmlDocument BuildResults(BatchRunResponse results)
        {
            var document = new HtmlDocument
            {
                Title = "Storyteller Batch Results for {0}: {1}".ToFormat(results.system, results.suite)
            };

            WriteCSS(document);
            writeJavascript(results, document);

            return document;
        }
        private static void writeInitialData(HtmlDocument document, Suite top, FixtureLibrary fixtures, string title)
        {
            ClientMessage[] initial = new ClientMessage[]
            {
                new FixturesReloaded {fixtures = fixtures.Models.ToArray()},
                new HierarchyLoaded(top, new ResultsCache()),
                new ProjectTitle(title),
            };

            var cleanJson = JsonSerialization.ToCleanJson(initial);
            document.Body.Add("div").Hide().Id("spec-data").Text(cleanJson);
        }
        private static void writeJavascript(BatchRunResponse results, HtmlDocument document)
        {
            var cleanJson = JsonSerialization.ToCleanJson(results);

            document.Body.Add("div").Hide().Id("batch-data").Text(cleanJson);
            document.Body.Add("div").Id("main");

            var js = readFile("StoryTeller.batch-bundle.js");

            document.Body.Add("script").Attr("language", "javascript").Text("\n\n" + js + "\n\n").Encoded(false);


        }
        private static void writeClientSideAssetsFromEmbeds(HtmlDocument document)
        {
            BatchResultsWriter.WriteCSS(document);
            document.Head.Add("link")
                .Attr("rel", "stylesheet")
                .Attr("href", "//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css");


            var bundleJS = typeof(HomeEndpoint).GetTypeInfo().Assembly
                .GetManifestResourceStream("StorytellerRunner.bundle.js").ReadAllText();

            var scriptTag = new HtmlTag("script").Attr("type", "text/javascript").Text(bundleJS).Encoded(false);
            document.Body.Append(scriptTag);
        }
        public static void WriteClientAssetsDebugMode(HtmlDocument document, bool devMode, string bundleName = "/bundle.js")
        {
            var stylesheets = new[] {"bootstrap.min.css", "storyteller.css", "font-awesome.min.css", "fixed-data-table.min.css"};
            var tags = stylesheets.Select(file =>
            {
                var path = $"/public/stylesheets/{file}";
                return new HtmlTag("link").Attr("rel", "stylesheet").Attr("href", path);
            });

            document.Head.Append(tags);

            var bundleUrl = devMode ? "http://localhost:3001/client/public/javascript/bundle.js" : bundleName;
            var scriptTag = new HtmlTag("script").Attr("type", "text/javascript").Attr("src", bundleUrl);
            document.Body.Append(scriptTag);
        }
        public HtmlDocument get__todo()
        {
            var document = new HtmlDocument
            {
                Title = "TODO's"
            };

            document.Head.Add("link").Attr("rel", "stylesheet").Attr("type", "text/css")
                .Attr("href", "/content/bootstrap.min.css");

            document.Push("div").AddClass("container");

            document.Add("h1").Text("TODO's");

            var todos = TodoTask.FindAllTodos(_top.AllTopicsInOrder().ToArray());

            document.Add(new TodoTableTag(todos));

            return document;
        }
        private static void writeInitialDataIntoPage(HtmlDocument document, IApplication application)
        {
            var cleanJson = JsonSerialization.ToCleanJson(application.Persistence.Hierarchy.Top);
            document.Body.Add("div").Hide().Id("hierarchy-data").Text(cleanJson);

            var resultJson = JsonSerialization.ToCleanJson(application.Persistence.AllCachedResults());
            document.Body.Add("div").Hide().Id("result-data").Text(resultJson);

            var model = application.BuildInitialModel();

            var script = new StringWriter();
            script.WriteLine();
            script.WriteLine($"var Storyteller = {{wsAddress: '{application.Client.WebSocketsAddress}'}};");
            script.WriteLine();
            script.WriteLine("Storyteller.initialization = {0};",
                JsonSerialization.ToCleanJson(model));
            script.WriteLine();


            document.Head.Add("script").Encoded(false).Text(script.ToString()).Attr("type", "text/javascript");
        }
        public static HtmlDocument BuildPage(IApplication application, OpenInput input)
        {
            var document = new HtmlDocument {Title = "Storyteller 4"};


            writeInitialDataIntoPage(document, application);

            document.Add("div").Id("header-container");
            document.Add("div").Id("body-pane").AddClass("container");
            document.Add("div").Id("main");

#if DEBUG
            WriteClientAssetsDebugMode(document, input.DevFlag);
#else

            writeClientSideAssetsFromEmbeds(document);
#endif


            return document;
        }
        public static HtmlDocument Build(Suite top, FixtureLibrary fixtures, string title)
        {
            var document = new HtmlDocument
            {
                Title = title
            };

            BatchResultsWriter.WriteCSS(document);

            writeInitialData(document, top, fixtures, title);

#if DEBUG
            HomeEndpoint.WriteClientAssetsDebugMode(document, false, "/preview.js");
#else
            writeEnbeddedJavascript(document);
#endif

            document.Body.Add("div").Id("main");

            return document;
        }