// Internal for unit testing purposes only.
        private int GetDefaultTeaserLength(ExcerptTruncationMode mode)
        {
            switch (mode)
            {
            case ExcerptTruncationMode.Length:
                return(defaultLengthAbsolute);

            case ExcerptTruncationMode.Character:
                return(defaultLengthCharacters);

            case ExcerptTruncationMode.Word:
            default:
                return(defaultLengthWords);
            }
        }
        // Internal for unit testing purposes only.
        private string TruncatePost(ExcerptTruncationMode mode, string content, int length, bool isRightToLeftLanguage = false)
        {
            var truncateFrom = isRightToLeftLanguage ? TruncateFrom.Left : TruncateFrom.Right;

            switch (mode)
            {
            case ExcerptTruncationMode.Length:
                return(content.Truncate(length, terminator, Truncator.FixedLength, truncateFrom));

            case ExcerptTruncationMode.Character:
                return(content.Truncate(length, terminator, Truncator.FixedNumberOfCharacters, truncateFrom));

            case ExcerptTruncationMode.Word:
            default:
                return(content.Truncate(length, terminator, Truncator.FixedNumberOfWords, truncateFrom));
            }
        }
        //private string GetModeDescription(ExcerptTruncationMode mode)
        //{
        //    switch (mode)
        //    {
        //        case ExcerptTruncationMode.Length:
        //            return "string length";
        //        case ExcerptTruncationMode.Character:
        //            return "letters or digits";
        //        case ExcerptTruncationMode.Word:
        //        default:
        //            return "words";
        //    }
        //}

        private int GetContentLength(string html, ExcerptTruncationMode mode)
        {
            if (string.IsNullOrEmpty(html))
            {
                return(0);
            }
            switch (mode)
            {
            case ExcerptTruncationMode.Length:
                return(html.Length);

            case ExcerptTruncationMode.Character:
                return(html.ToCharArray().Count(char.IsLetterOrDigit));

            case ExcerptTruncationMode.Word:
            default:
                return(html.Split((char[])null, StringSplitOptions.RemoveEmptyEntries).Count());
            }
        }
        /// <summary>
        /// this is an expensive method as often many retries ar eneeded to produce a valid html fragment.
        /// therefore this should not be used for dynamic excerpt generation.
        /// Excerpt should be generated and saved when content is edited.
        /// </summary>
        /// <param name="truncationMode"></param>
        /// <param name="truncationLength"></param>
        /// <param name="html"></param>
        /// <param name="languageCode"></param>
        /// <returns></returns>
        public ExcerptResult GenerateExcerpt(
            ExcerptTruncationMode truncationMode,
            int truncationLength,
            string html,
            //string cacheKey,
            //string slug,
            string languageCode //,
            //bool logWarnings = true
            )
        {
            var result = new ExcerptResult();

            if (string.IsNullOrWhiteSpace(html))
            {
                result.HtmlContent = html;
                result.DidTruncate = false;
                return(result);
            }

            // Try to get language metadata
            var cultureInfo = CultureInfo.InvariantCulture;

            if (!string.IsNullOrEmpty(languageCode))
            {
                try
                {
                    cultureInfo = new CultureInfo(languageCode);
                }
                catch (CultureNotFoundException) { }
            }

            var contentLength = GetContentLength(html, truncationMode);

            if (contentLength <= truncationLength)
            {
                result.HtmlContent = html;
                result.DidTruncate = false;
                return(result);
            }

            //if (_cache != null)
            //{
            //    var cachedTeaser = _cache.GetTeaser(cacheKey);
            //    if (!string.IsNullOrEmpty(cachedTeaser))
            //    {
            //        result.Content = cachedTeaser;
            //        result.DidTruncate = true;
            //        return result;
            //    }
            //}

            var isRightToLeftLanguage = cultureInfo.TextInfo.IsRightToLeft;

            // Get global teaser settings.
            var truncationLengthToUse = truncationLength <= 0 ? GetDefaultTeaserLength(truncationMode) : truncationLength;

            // Truncate the raw content first. In general, Humanizer is smart enough to ignore tags, especially if using word truncation.
            var text = TruncatePost(truncationMode, html, truncationLengthToUse, isRightToLeftLanguage);

            // Don't leave dangling <p> tags.
            HtmlNode.ElementsFlags["p"] = HtmlElementFlag.Closed;

            //var modeDesc = GetModeDescription(truncationMode);

            //if we get bad output try increasing the allowed length unti it is valid
            while (!IsValidMarkup(text) && truncationLengthToUse <= contentLength)
            {
                truncationLengthToUse += 1;
                //if (_log != null && logWarnings)
                //{
                //    _log.LogWarning($"teaser truncation for post {slug}, produced invalid html, so trying again and increasing the truncation length to {truncationLengthToUse} {modeDesc}. You should re-publish this post to create a persistent teaser.");
                //}

                text = TruncatePost(truncationMode, html, truncationLengthToUse, isRightToLeftLanguage);
            }

            if (!IsValidMarkup(text))
            {
                //if (_log != null)
                //{
                //    _log.LogError($"failed to create valid teaser for post {slug}, so returning full content");
                //}

                result.HtmlContent = html;
                result.DidTruncate = false;
                return(result);
            }

            //if (_cache != null)
            //{
            //    _cache.AddToCache(text, cacheKey);
            //}

            var doc = new HtmlDocument();

            doc.LoadHtml(text);

            result.HtmlContent = doc.DocumentNode.InnerHtml;
            result.DidTruncate = true;
            return(result);
        }