using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CssSpriteGenerator
{
    /// <summary>
    /// Represents a stylesheet. Allows you to add definitions and to get the entire sheet as a string.
    /// </summary>
    public class Stylesheet
    {
        private ConfigSection _cs = null;
        private int _classSequenceNbr = 0;

        // Used to store the declarations added via AddDefinition
        private StringBuilder _declarations = new StringBuilder();

        // Used to store definitions and their classes. Only used by AddDeclaration.
        // The key is the defintion, and the value is the class.
        private Dictionary<string, string> _definitionToClass = new Dictionary<string, string>();

        public Stylesheet(ConfigSection cs)
        {
            _cs = cs;
        }

        /// <summary>
        /// Adds a CSS declaration to the stylesheet. 
        /// A declaration is a bit with the properties, so without the selector.
        /// Don't include the surrounding accolades.
        /// 
        /// This method will create a class name and use that class as the selector.
        /// If the declaration already exists in the stylesheet, that declaration's class will be used.
        /// 
        /// Returns the name of the class.
        /// </summary>
        /// <param name="declaration"></param>
        /// <returns></returns>
        public string AddDeclaration(string declaration)
        {
            string className = null;

            if (_definitionToClass.ContainsKey(declaration))
            {
                // Declaration is already in the stylesheet. Return its class.
                className = _definitionToClass[declaration];
            }
            else
            {
                // Create new unique class name
                className = ClassName(_cs.ClassPostfix, _classSequenceNbr);
                _classSequenceNbr++;

                // Add class and declaration to the dictionary
                _definitionToClass[declaration] = className;
            }

            return className;
        }

        /// <summary>
        /// Adds a full definition.
        /// </summary>
        /// <param name="selector">
        /// The selector to use.
        /// </param>
        /// <param name="declaration">
        /// The declaration, without the accolades.
        /// </param>
        public void AddDefinition(string selector, string declaration)
        {
            _declarations.Append(CssDefinition(selector, declaration));
        }

        /// <summary>
        /// Writes the CSS definitions contained in this object to a css file.
        /// Responsible for determining what css file name to use.
        /// 
        /// Note that each page in the site may have its own unique css file.
        /// 
        /// The name is based on the contents of the file, so if two pages
        /// use the same css definitions, the same css file name is produced,
        /// so the browser may find the css file already in cache.
        /// 
        /// If there are no definitions - that is, the file would be empty -
        /// doesn't write the file and returns null.
        /// </summary>
        /// <param name="fileSystem">
        /// This object will be used to write the sprite image to the sprites folder.
        /// </param>
        /// <returns>
        /// The url of the new css file
        /// </returns>
        public string WriteFile(IFileSystem fileSystem)
        {
            string fileContents = ToString();
            if (string.IsNullOrWhiteSpace(fileContents)) { return null; }

            string fileName = UrlUtils.StringHash(fileContents) + ".css";

            string url = fileSystem.WriteTextFile(fileName, fileContents);
            return url;
        }

        /// <summary>
        /// Returns a link html tag to load a CSS file with the given url.
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string CssLink(string url)
        {
            string cssLink = string.Format("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\"/>", url);
            return cssLink;
        }

        /// <summary>
        /// Returns the entire stylesheet as a string.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            // Store all defintions in this StringBuilder
            StringBuilder stylesheetSb = new StringBuilder();
            
            // First append all definitions added via AddDefinition
            stylesheetSb.Append(_declarations.ToString());

            // Then append all definitions added via AddDeclaration
            foreach (KeyValuePair<String, String> definitionClass in _definitionToClass)
            {
                string declaration = definitionClass.Key;
                string className = definitionClass.Value;

                stylesheetSb.Append(CssDefinition("." + className, declaration));
            }

            string finalStylesheet = stylesheetSb.ToString();
            return finalStylesheet;
        }

        private string ClassName(string classPostFix, int classSequenceNbr)
        {
            string className = string.Format("c{0}{1}", classSequenceNbr, classPostFix);
            return className;
        }

        private string CssDefinition(string selector, string declaration)
        {
            string definition = string.Format("{0} {{{1}}} ", selector, declaration);
            return definition;
        }
    }
}
