internal void Generate ()
        {
            using (var tagsConnection = new SqlConnection(_settings.DatabaseConnectionString))
            using (var postsConnection = new SqlConnection (_settings.DatabaseConnectionString))
            {
                tagsConnection.Open ();
                postsConnection.Open ();
                var tagRepository = new TagRepository (tagsConnection);
                var postRepository = new PostRepository (postsConnection);

                #region Phase 1: Convert Posts (collection items) into temporary raw artifacts
                CleanWorkingFolder ();
                CreateRawItems (postRepository);
                GeneratePostImageResizes (postRepository);
                #endregion

                #region Phase 2: Slice Post (collection item) images to create final .dzi files and sub-folders
                GenerateImageSlices (postRepository);
                GenerateImageManifests (postRepository);
                #endregion

                #region Phase 3: Convert Tags (collections) into final .cxml and .dzc files
                AssembleCollections (tagRepository, postRepository);
                #endregion
            }
        }
        internal void CreateRawItems (PostRepository postRepository)
        {
            var workingPath = _settings.AbsoluteWorkingFolder;
            Directory.CreateDirectory (workingPath);
            Page template;
            using (var stream = AssemblyExtensions.OpenScopedResourceStream<Program> ("Template.xaml"))
            {
                template = (Page) XamlReader.Load (stream);
                template.Width = _settings.ItemImageSize.Width;
                template.Height = _settings.ItemImageSize.Height;
            }
            var imageFormat = _settings.PostImageEncoding;
            var imageExtension = imageFormat.GetName ();

            var posts = postRepository.RetrievePosts ();
            foreach (var post in posts)
            {
                var relativeBinnedXmlPath = post.ComputeBinnedPath (".xml", _settings.FileNameIdFormat);
                var absoluteBinnedXmlPath = Path.Combine (workingPath, relativeBinnedXmlPath);
                Directory.CreateDirectory (Path.GetDirectoryName (absoluteBinnedXmlPath));
                var element = PivotizePost (post);
                using (var outputStream = absoluteBinnedXmlPath.CreateWriteStream ())
                {
                    using (var writer = new ItemWriter (outputStream, _settings.XmlWriterSettings))
                    {
                        element.Save (writer);
                    }
                }

                var relativeBinnedImagePath = post.ComputeBinnedPath (imageExtension, _settings.FileNameIdFormat);
                var absoluteBinnedImagePath = Path.Combine (workingPath, relativeBinnedImagePath);
                Directory.CreateDirectory (Path.GetDirectoryName (absoluteBinnedImagePath));
                using (var outputStream = absoluteBinnedImagePath.CreateWriteStream ())
                {
                    ImagePost (post, template, imageFormat, outputStream);
                }
            }
        }
        internal void GeneratePostImageResizes (PostRepository postRepository)
        {
            var dzi = new DeepZoomImage (_settings);

            var workingPath = _settings.AbsoluteWorkingFolder;
            var imageFormat = _settings.PostImageEncoding;
            var extension = imageFormat.GetName ();
            var fileNameIdFormat = _settings.FileNameIdFormat;
            foreach (var postId in postRepository.RetrievePostIds ())
            {
                var relativeBinnedImagePath = Post.ComputeBinnedPath (postId, extension, fileNameIdFormat);
                var absoluteBinnedImagePath = Path.Combine (workingPath, relativeBinnedImagePath);
                var relativeBinnedImageFolder = Post.ComputeBinnedPath (postId, null, fileNameIdFormat) + "_files";
                var absoluteBinnedImageFolder = Path.Combine (workingPath, relativeBinnedImageFolder);
                Directory.CreateDirectory (absoluteBinnedImageFolder);
                using (var inputStream = absoluteBinnedImagePath.CreateReadStream ())
                using (var sourceBitmap = new Bitmap (inputStream))
                {
                    dzi.GeneratePostImageResizes (sourceBitmap, (level, resizedBitmap) =>
                        {
                            var levelImageName = "{0}.{1}".FormatInvariant (level, extension);
                            var levelImagePath = Path.Combine (absoluteBinnedImageFolder, levelImageName);
                            using (var outputStream = levelImagePath.CreateReadStream ())
                            {
                                resizedBitmap.Save (outputStream, imageFormat);
                            }
                        }
                    );
                }
            }
        }
 internal void GenerateImageSlices(PostRepository postRepository)
 {
     var dzi = new DeepZoomImage (_settings);
     foreach (var postId in postRepository.RetrievePostIds ())
     {
         dzi.SlicePostImage (postId);
     }
 }
        internal void AssembleCollections (TagRepository tagRepository, PostRepository postRepository)
        {
            var dzc = new DeepZoomCollection (_settings);

            var tags = tagRepository.RetrieveTags ();
            foreach (var tag in tags)
            {
                var postIds = new List<int> (postRepository.RetrievePostIds (tag.Id));
                dzc.PivotizeTag (tag, postIds);
                dzc.CreateCollectionManifest (tag, postIds);
                dzc.CreateCollectionTiles (tag, postIds);
            }
        }
        public static int Main (string[] args)
        {
            // TODO: initialize Settings instance from app.config and/or command-line
            var settings = new Settings
            {
                ItemImageSize = new Size(800, 400),
                TileSize = 254,
                TileOverlap = 1,
                /*
                DatabaseConnectionString = "Data Source=Blackberry;Initial Catalog=SuperUser;Integrated Security=True",
                SiteDomain = "superuser.com",
                MaximumNumberOfItems = 185520,
                HighestId = 207698,
                 */
                DatabaseConnectionString = "Data Source=Blackberry;Initial Catalog=Photography;Integrated Security=True",
                SiteDomain = "photo.stackexchange.com",
                MaximumNumberOfItems = 936,
                HighestId = 4479,

                PostImageEncoding = ImageFormat.Png,
            };

            using (var tagsConnection = new SqlConnection(settings.DatabaseConnectionString))
            using (var postsConnection = new SqlConnection (settings.DatabaseConnectionString))
            {
                tagsConnection.Open ();
                postsConnection.Open ();
                var tagRepository = new TagRepository (tagsConnection);
                var postRepository = new PostRepository (postsConnection);

                #region Phase 1: Convert Posts (collection items) into temporary raw artifacts
                CleanWorkingFolder ();
                CreateRawItems (settings, postRepository);
                GeneratePostImageResizes (settings, postRepository);
                #endregion

                #region Phase 2: Slice Post (collection item) images to create final .dzi files and sub-folders
                GenerateImageSlices (settings, postRepository);
                GenerateImageManifests (settings, postRepository);
                #endregion

                #region Phase 3: Convert Tags (collections) into final .cxml and .dzc files
                AssembleCollections (settings, tagRepository, postRepository);
                #endregion
            }
            return 0;
        }
        internal void GenerateImageManifests (PostRepository postRepository)
        {
            var fileNameIdFormat = _settings.FileNameIdFormat;
            var imageNode = _settings.GenerateImageManifest ();

            var sb = new StringBuilder ();
            using (var writer = XmlWriter.Create (sb, _settings.XmlWriterSettings))
            {
                Debug.Assert (writer != null);
                imageNode.WriteTo (writer);
            }
            var imageManifest = sb.ToString ();

            foreach (var postId in postRepository.RetrievePostIds ())
            {
                var relativeBinnedImageManifestPath = Post.ComputeBinnedPath (postId, "dzi", fileNameIdFormat);
                var absoluteBinnedImageManifestPath =
                    Path.Combine (_settings.AbsoluteOutputFolder, relativeBinnedImageManifestPath);
                Directory.CreateDirectory (Path.GetDirectoryName (absoluteBinnedImageManifestPath));
                File.WriteAllText (absoluteBinnedImageManifestPath, imageManifest, Encoding.UTF8);
            }
        }
 internal static void PivotizeTag (PostRepository postRepository, Tag tag, Settings settings)
 {
     var workingPath = Path.GetFullPath (WorkingFolderName);
     var outputPath = Path.GetFullPath (OutputFolderName);
     var relativeBinnedCxmlPath = tag.ComputeBinnedPath (".cxml");
     var absoluteBinnedCxmlPath = Path.Combine (outputPath, relativeBinnedCxmlPath);
     Directory.CreateDirectory (Path.GetDirectoryName (absoluteBinnedCxmlPath));
     using (var outputStream
         = new FileStream (absoluteBinnedCxmlPath, FileMode.Create, FileAccess.Write, FileShare.Read))
     {
         var postIds = postRepository.RetrievePostIds (tag.Id);
         var streamReaders = postIds.Map (postId =>
             {
                 var relativeBinnedXmlPath = Post.ComputeBinnedPath (postId, ".xml", settings.FileNameIdFormat);
                 var absoluteBinnedXmlPath = Path.Combine (workingPath, relativeBinnedXmlPath);
                 var sr = new StreamReader (absoluteBinnedXmlPath);
                 return sr;
             }
         );
         PivotizeTag (tag, streamReaders, outputStream, settings.SiteDomain);
     }
 }
        internal static void GeneratePostImageResizes (Settings settings, PostRepository postRepository)
        {
            var size = settings.ItemImageSize;
            var maximumLevel = DeepZoomImage.DetermineMaximumLevel (size);

            var workingPath = Path.GetFullPath (WorkingFolderName);
            var imageFormat = settings.PostImageEncoding;
            var extension = imageFormat.ToString ().ToLower ();
            var fileNameIdFormat = settings.FileNameIdFormat;
            foreach (var postId in postRepository.RetrievePostIds ())
            {
                var relativeBinnedImagePath = Post.ComputeBinnedPath (postId, extension, fileNameIdFormat);
                var absoluteBinnedImagePath = Path.Combine (workingPath, relativeBinnedImagePath);
                var relativeBinnedImageFolder = Post.ComputeBinnedPath (postId, null, fileNameIdFormat) + "_files";
                var absoluteBinnedImageFolder = Path.Combine (workingPath, relativeBinnedImageFolder);
                Directory.CreateDirectory (absoluteBinnedImageFolder);
                using (var inputStream =
                    new FileStream (absoluteBinnedImagePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var sourceBitmap = new Bitmap (inputStream))
                {
                    GeneratePostImageResizes (sourceBitmap, size, maximumLevel, (level, resizedBitmap) =>
                        {
                            var levelImageName = "{0}.{1}".FormatInvariant (level, extension);
                            var levelImagePath = Path.Combine (absoluteBinnedImageFolder, levelImageName);
                            using (var outputStream =
                                new FileStream (levelImagePath, FileMode.Create, FileAccess.Write, FileShare.Read))
                            {
                                resizedBitmap.Save (outputStream, imageFormat);
                            }
                        }
                    );
                }
            }
        }
        internal static void AssembleCollections (Settings settings, TagRepository tagRepository, PostRepository postRepository)
        {
            var outputPath = Path.GetFullPath (OutputFolderName);

            var imageFormat = settings.PostImageEncoding;
            var imageFormatName = imageFormat.ToString ().ToLower ();
            var fileNameIdFormat = settings.FileNameIdFormat;
            var width = settings.ItemImageSize.Width;
            var height = settings.ItemImageSize.Height;

            var tags = tagRepository.RetrieveTags ();
            foreach (var tag in tags)
            {
                // TODO: consider using postIds, currently computed below
                PivotizeTag (postRepository, tag, settings);

                var postIds = new List<int> (postRepository.RetrievePostIds (tag.Id));

                var relativePathToCollectionManifest = Tag.ComputeBinnedPath (tag.Name, ".dzc");
                var absolutePathToCollectionManifest = Path.Combine (outputPath, relativePathToCollectionManifest);
                var relativePathToRoot = relativePathToCollectionManifest.RelativizePath ();

                CreateCollectionManifest (postIds, absolutePathToCollectionManifest, imageFormatName, relativePathToRoot,
                                          fileNameIdFormat, width, height);

                CreateCollectionTiles (tag, outputPath, postIds, imageFormat, fileNameIdFormat, outputPath);
            }
        }
 internal static void GenerateImageSlices(Settings settings, PostRepository postRepository)
 {
     var size = settings.ItemImageSize;
     var maximumLevel = DeepZoomImage.DetermineMaximumLevel (size);
     var imageFormat = settings.PostImageEncoding;
     var imageExtension = imageFormat.ToString ().ToLower ();
     var fileNameIdFormat = settings.FileNameIdFormat;
     foreach (var postId in postRepository.RetrievePostIds ())
     {
         SlicePostImage (postId, size, maximumLevel, imageExtension, fileNameIdFormat, imageFormat, settings.TileSize, settings.TileOverlap);
     }
 }
        internal static void GenerateImageManifests (Settings settings, PostRepository postRepository)
        {
            var outputPath = Path.GetFullPath (OutputFolderName);
            var fileNameIdFormat = settings.FileNameIdFormat;
            var imageNode = GenerateImageManifest (settings.TileSize, settings.TileOverlap,
                                                   settings.PostImageEncoding.ToString ().ToLower (),
                                                   settings.ItemImageSize.Width, settings.ItemImageSize.Height);

            var sb = new StringBuilder ();
            using (var writer = XmlWriter.Create (sb, WriterSettings))
            {
                Debug.Assert (writer != null);
                imageNode.WriteTo (writer);
            }
            var imageManifest = sb.ToString ();

            foreach (var postId in postRepository.RetrievePostIds ())
            {
                var relativeBinnedImageManifestPath = Post.ComputeBinnedPath (postId, "dzi", fileNameIdFormat);
                var absoluteBinnedImageManifestPath = Path.Combine (outputPath, relativeBinnedImageManifestPath);
                Directory.CreateDirectory (Path.GetDirectoryName (absoluteBinnedImageManifestPath));
                File.WriteAllText (absoluteBinnedImageManifestPath, imageManifest, Encoding.UTF8);
            }
        }