using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.IO;
using System.Diagnostics;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace CssSpriteGenerator
{
    public class ControlUtils
    {
        /// <summary>
        /// Returns the html that will be generated for a control by ASP.NET.
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static string ControlHtml(Control c)
        {
            Debug.Assert(c != null);

            string html = null;

            if (c is LiteralControl)
            {
                html = ((LiteralControl)c).Text;
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb);
                HtmlTextWriter htw = new HtmlTextWriter(sw);

                c.RenderControl(htw);

                html = sb.ToString();
            }
            
            return html;
        }

        /// <summary>
        /// Gets the html that will be generated for a control. Looks in that html to see
        /// if it contains an image tag. 
        /// 
        /// If there is one, returns an ImageTag object
        /// with the details of the image.
        /// 
        /// If there are no images, returns null.
        /// 
        /// If there are multiple images, returns the first image.
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static ImageTag ImageInControl(Control c)
        {
            string html = ControlHtml(c);
            IList<ImageTag> imageTags = HtmlUtils.ImagesInHtml(html);

            if ((imageTags == null) || (imageTags.Count == 0))
            {
                return null;
            }

            return imageTags[0];
        }


        /// <summary>
        /// Every control is a member of the Controls collection of the page, or a member of
        /// the Controls collection of one of the other
        /// controls on the page.
        /// 
        /// This method removes the given control from its containing collection, and
        /// inserts a new LiteralControl control at the location where it used to be.
        /// That LiteralControl contains the html for a sprite.
        /// 
        /// If the control is already a LiteralControl, this method simply modifies the LiteralControl
        /// without removing it from the parent collection.
        /// 
        /// Note that the control may contain more than only an image. For example, a hyperlink
        /// contains both an a tag and an img tag. So this method replaces only the img tag with the sprite html.
        /// </summary>
        /// <param name="c">
        /// Control to be removed
        /// </param>
        /// <param name="pageSpriteHtml">
        /// Html of the sprite
        /// </param>
        /// <param name="originalImgTagText">
        /// Original html of the image tag that is being replaced by a sprite
        /// </param>
        public static void ReplaceControlWithSprite(Control c, string pageSpriteHtml, string originalImgTagText)
        {
            string newHtml = null;

            if ((c is HtmlImage) || (c is Image))
            {
                // When you render an HtmlImage for the second time (by calling ControlHtml),
                // the src attribute is not rendered! However, with an HtmlImage, you can
                // replace all its html, because it is purely an image and doesn't 
                // contain other stuff as with a Hyperlink control.
                //
                // The Image control doesn't seem to have this problem. But seeing that it too
                // is purely an image, there is no need to get the original html again.

                newHtml = pageSpriteHtml;
            }
            else
            {
                // With Hyperlink and LiteralControl controls, there is html in the control besides the image,
                // so need to take care to replace only the image. Note that with LiteralControl controls,
                // the same LiteralControl may have bits replaced multiple times, because a LiteralControl can
                // contain multiple img tags.

                string originalHtml = ControlHtml(c);
                newHtml = originalHtml.Replace(originalImgTagText, pageSpriteHtml);
            }

            if (c is LiteralControl)
            {
                LiteralControl existingLiteralControl = (LiteralControl)c;
                existingLiteralControl.Text = newHtml;
            }
            else
            {
                LiteralControl newLiteralControl = new LiteralControl(newHtml);
                ReplaceControl(c, newLiteralControl);
            }
        }

        /// <summary>
        /// Every control is a member of the Controls collection of the page, or a member of
        /// the Controls collection of one of the other
        /// controls on the page.
        /// 
        /// This method removes the given control from its containing collection, and
        /// inserts a new control at the location where it used to be.
        /// </summary>
        /// <param name="oldControl">Control to be removed</param>
        /// <param name="newControl">Control that comes in the place where oldControl used to be</param>
        private static void ReplaceControl(Control oldControl, Control newControl)
        {
            Control parent = oldControl.Parent;
            ControlCollection cc = parent.Controls;

            int idxControl = cc.IndexOf(oldControl);

            Debug.Assert(idxControl > -1);

            if (idxControl > -1)
            {
                cc.RemoveAt(idxControl);
                cc.AddAt(idxControl, newControl);
            }
        }
    }
}
