//
//  DescriptionSectionUtility.h
//  snowcrash
//
//  Created by Zdenek Nemec on 11/10/13.
//  Copyright (c) 2013 Apiary Inc. All rights reserved.
//

#ifndef SNOWCRASH_DESCRIPTIONSECTIONUTILITY_H
#define SNOWCRASH_DESCRIPTIONSECTIONUTILITY_H

#include <sstream>
#include "MarkdownBlock.h"
#include "BlueprintParserCore.h"
#include "CodeBlockUtility.h"

namespace snowcrash {
    
    
    /**
     *  \brief  Skips to the end of the description list.
     *  \param  begin   Begin of the description list.
     *  \param  end     End of the block buffer.
     *  \param  descriptionMap  Output buffer containing skipped source map.
     *  \return An iterator pointing to the last block of a description list.
     *          Or, if one of description list items is recognized as a section, iterator
     *          pointing to this section.
     *
     *  This functions checks any skipped list items for a section signature.
     *  If a section signature is found this function returns the first block
     *  of the recognized section. If no signature is found this function returns
     *  the last (closing) block of the description list.
     */
    template <class T>
    static BlockIterator SkipToDescriptionListEnd(const BlockIterator& begin,
                                                  const BlockIterator& end,
                                                  SourceDataBlock& descriptionMap) {
        BlockIterator cur(begin);
        if (++cur == end)   // Skip leading ListBlockBeginType
            return cur;
        
        std::vector<SourceDataBlock> listItemMaps;
        SectionType listItemSection = UndefinedSectionType;
        BlockIterator recognizedCur = end;
        
        while (cur != end &&
               cur->type == ListItemBlockBeginType) {
            
            // Classify list item
            listItemSection = ClassifyInternaListBlock<T>(cur, end);
            if (listItemSection != UndefinedSectionType) {
                // Found a recognized section, record & skip to the end of the list.
                recognizedCur = cur;
                cur = SkipToClosingBlock(begin, end, ListBlockBeginType, ListBlockEndType);
            }
            else {
                // Skip one list item & take note of its source map
                cur = SkipToClosingBlock(cur, end, ListItemBlockBeginType, ListItemBlockEndType);
                listItemMaps.push_back(cur->sourceMap);
                
                if (cur != end)
                    ++cur;
            }
        }
        
        // Resolve
        if (listItemSection == UndefinedSectionType) {
            descriptionMap = cur->sourceMap;
            return cur;
        }
        else {
            // Resolve correct description source map
            descriptionMap.clear();
            if (!cur->sourceMap.empty() &&
                !listItemMaps.empty() &&
                !listItemMaps.front().empty()) {
                
                SourceDataRange r;
                r.location = cur->sourceMap.front().location;
                r.length = listItemMaps.front().front().location - r.location;
                descriptionMap.push_back(r);
                
                for (std::vector<SourceDataBlock>::iterator it = listItemMaps.begin();
                     it != listItemMaps.end();
                     ++it) {
                    
                    SourceDataRange gap;
                    gap.location = descriptionMap.back().location + descriptionMap.back().length;
                    gap.length = it->front().location - gap.location;
                    if (gap.length) {
                        SourceDataBlock gapBlock(1, gap);
                        AppendSourceDataBlock(descriptionMap, gapBlock);
                    }
                    
                    AppendSourceDataBlock(descriptionMap, *it);
                }
            }
            
            return recognizedCur;
        }
    }
    
    /**
     *  \brief  Process a description block retrieving its content.
     *  \param  section     A section its block is being processed.
     *  \param  cur         Cursor to the block to process.
     *  \param  sourceData  Source data stream.
     *  \param  output      Output object to APPEND retrieved description into.
     *  \return Standard parser section result poinitng at the last block parsed.
     */
    template <class T>
    FORCEINLINE ParseSectionResult ParseDescriptionBlock(const BlueprintSection& section,
                                                         const BlockIterator& cur,
                                                         const SourceData& sourceData,
                                                         T& output) {
        
        ParseSectionResult result = std::make_pair(Result(), cur);
        BlockIterator sectionCur(cur);
        
        if (sectionCur->type == QuoteBlockBeginType) {
            sectionCur = SkipToClosingBlock(sectionCur, section.bounds.second, QuoteBlockBeginType, QuoteBlockEndType);
        }
        else if (sectionCur->type == ListBlockBeginType) {
            
            SourceDataBlock descriptionMap;
            sectionCur = SkipToDescriptionListEnd<T>(sectionCur, section.bounds.second, descriptionMap);

            if (sectionCur->type != ListBlockEndType) {
                // Found recognized lists in the list block
                if (!descriptionMap.empty())
                    output.description += MapSourceData(sourceData, descriptionMap);
                
                result.second = sectionCur;
                return result;
            }
        }
        else if (sectionCur->type == CodeBlockType) {
            // Check code block for potential excessive indentation of a list item
            CheckCodeBlockListItem<T>(section, sectionCur, sourceData, result.first);
        }
        else if (sectionCur->type == HeaderBlockType) {
            // Check headers for potential keywords
            CheckHeaderBlock<T>(section, sectionCur, sourceData, result.first);
        }
        
        if (!CheckCursor(section, sectionCur, sourceData, result.first))
            return result;

        output.description += MapSourceData(sourceData, sectionCur->sourceMap);
        result.second = ++sectionCur;
        
        return result;
    }
}

#endif