using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging; // for ImageFormat class

namespace TileGenerator
{
    /// <summary>
    /// Take a large image and generate quadkey-indexed tiles to a defined zoom level
    /// </summary>
    class Program
    {
        const int TILE_SIZE = 256;

        const string SOURCE_IMAGE = @"C:\Images\venus-whole.jpg"; // 5
        const string TARGET_IMAGE_PREFIX = "c";
        const int MAX_ZOOM = 5; 

        const string TARGET_PATH = @"C:\Inetpub\TileClient06\Tiles\";
        

        static void Main(string[] args)
        {
            int rowCount, colCount;
            for (int zoom = 1; zoom <= MAX_ZOOM; zoom++) 
            {
                rowCount = colCount = (int)Math.Pow(Convert.ToDouble(2), Convert.ToDouble(zoom));

                Console.WriteLine("ZOOM: " + zoom + " " + rowCount + "," + colCount);
                Bitmap zoomBitmap = LoadImageForZoom (SOURCE_IMAGE, zoom);

                for (int col = 0; col < colCount; col++)
                {
                    Console.Write(" Row: ");
                    for (int row = 0; row < rowCount; row++)    
                    {
                        string quadKey = TileXYToQuadKey(row, col, zoom);
                        CropImage(zoomBitmap, row * TILE_SIZE, col * TILE_SIZE, quadKey);
                        Console.Write(quadKey + " ");
                    }
                    Console.WriteLine(" ");
                }
                Console.WriteLine("---");
            }
            Console.ReadKey();
        }

        /// <summary>
        /// Converts tile XY coordinates into a QuadKey at a specified level of detail.
        /// </summary>
        /// <remarks>
        /// Code (c) Microsoft
        /// http://msdn2.microsoft.com/en-us/library/bb259689.aspx
        /// </remarks>
        /// <param name="tileX">Tile X coordinate.</param>
        /// <param name="tileY">Tile Y coordinate.</param>
        /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail)
        /// to 23 (highest detail).</param>
        /// <returns>A string containing the QuadKey.</returns>
        public static string TileXYToQuadKey(int tileX, int tileY, int levelOfDetail)
        {
            StringBuilder quadKey = new StringBuilder();
            for (int i = levelOfDetail; i > 0; i--)
            {
                char digit = '0';
                int mask = 1 << (i - 1);
                if ((tileX & mask) != 0)
                {
                    digit++;
                }
                if ((tileY & mask) != 0)
                {
                    digit++;
                    digit++;
                }
                quadKey.Append(digit);
            }
            return quadKey.ToString();
        }
        /// <summary>
        /// Open the source image, resize it to the correct dimensions for the given zoom
        /// level (2^zoom) gives the number of rows/columns, then multiply by TileSize. 
        /// NOTE: will stretch image if it is too small.
        /// </summary>
        /// <remarks>
        /// Original code (c) Bob Powell
        /// http://www.bobpowell.net/changing_resolution.htm
        /// </remarks>
        public static Bitmap LoadImageForZoom (string imagePath, int zoom)
        {
            int imageSize = (int)Math.Pow(2, zoom) * TILE_SIZE;
            if (imageSize > int.MaxValue) throw new ArgumentException("Cannot create an image with dimesion " + imageSize);
            Bitmap bm = (Bitmap)Image.FromFile(imagePath);
            Bitmap resized = new Bitmap(imageSize, imageSize); //16384
            Graphics g = Graphics.FromImage(resized);
            g.DrawImage(bm, new Rectangle(0, 0, resized.Width, resized.Height), 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel);
            g.Dispose();
            return resized;
        }
        /// <summary>
        /// Crop the supplied image and save it as a tile
        /// </summary>
        /// <remarks>
        /// Original code (c) Bob Powell
        /// http://www.bobpowell.net/changing_resolution.htm
        /// </remarks>
        public static void CropImage(Bitmap image, int imageX, int imageY, string quadKey)
        {
            Bitmap cropped = new Bitmap(TILE_SIZE, TILE_SIZE);
            Graphics g = Graphics.FromImage(cropped);
            g.DrawImage(image, new Rectangle(0, 0, cropped.Width, cropped.Height), imageX, imageY, cropped.Width, cropped.Height, GraphicsUnit.Pixel);
            g.Dispose();
            cropped.Save(TARGET_PATH + TARGET_IMAGE_PREFIX + quadKey + ".jpg", ImageFormat.Jpeg);
        }
    }
}