/*
 * md ast - pluggable markdown parser
 */


const syntax = {
    bold: {
        paired: true,
        recursive: true,
        startRegexp: /\*\*\S.*/,
        endRegexp:   /\*\*\W/,
        content: {
            start: {
                point: 'start',
                offset: 2
            },
            end: {
                point: 'start',
                offset: 0
            }
        },
        begin: 0,
        forward: {
            point: 'endEnd', //start, startEnd, end, endEnd
            offset: -1
        }
    },
    bold2: {
        paired: true,
        recursive: true,
        startRegexp:  /\s__\S.*/,
        endRegexp:    /__\W/,
        content: {
            start: {
                point: 'start',
                offset: 3
            },
            end: {
                point: 'start',
                offset: 0
            }
        },
        begin: 1,
        forward: {
            point: 'endEnd', //start, startEnd, end, endEnd
            offset: -1
        }
    },
    italic: {
        paired: true,
        recursive: true,
        startRegexp: /\*\S.*/,
        endRegexp:   /\S\*\W/,
        content: {
            start: {
                point: 'start',
                offset: 1
            },
            end: {
                point: 'start',
                offset: 1
            }
        },
        begin: 0,
        forward: {
            point: 'endEnd', //start, startEnd, end, endEnd
            offset: -1
        }
    },
    italic2: {
        paired: true,
        recursive: true,
        startRegexp:  /\s_\S.*/,
        endRegexp:   /\S_\W/,
        content: {
            start: {
                point: 'start',
                offset: 2
            },
            end: {
                point: 'start',
                offset: 1
            }
        },
        begin: 1,
        forward: {
            point: 'endEnd', //start, startEnd, end, endEnd
            offset: -1
        }
    },
    root: {
        paired: true,
        recursive: true,
        startRegexp:  /^/,
        endRegexp:   /$/,
        content: {
            start: {
                point: 'start',
                offset: 1
            },
            end: {
                point: 'end',
                offset: 0
            }
        },
        begin: 0,
        forward: {
            point: 'endEnd', //start, startEnd, end, endEnd
            offset: -1
        }
    },
    heading1: {
        paired: true,
        recursive: true,
        startRegexp: /\n#[ \t]*(.*)\n/,
        endRegexp: /\n#/,
        content: {
            start: {
                point: 'end',
                offset: 0
            },
            end: {
                point: 'start',
                offset: 0
            }
        },
        begin: 1,
        forward: {
            point: 'end', //start, startEnd, end, endEnd
            offset: 0
        }
    }
}

function findNearest(md, mdTags, offset=0){
    let nearest, nearestMatch = {index: Infinity};
    for (let [mdTag, {paired, 
                        startRegexp, 
                        rexexp}] of  Object.entries(mdTags)) {
        if (mdTag === 'root') continue;

        let match = md.offsetMatch(offset, startRegexp || regexp)
        if (match && match.index < nearestMatch.index){
            nearestMatch = match
            nearest = mdTag
        }
    }
    return [nearest, nearestMatch]
}


//node:
//{
//     tag: 'keyFromSyntax',
//     children: [String,  Node]
//     parent: node
//}
//
String.prototype.offsetMatch = function(offset, ...params){
    return this.slice(offset).match(...params)
}

Array.prototype.last = function(){
    return this[this.length -1]
}

function buildAST(md, mdTags=syntax, offset=0, tree={tag: 'root'}, stack=[]){
    const currentNode     = stack.last() || tree
    currentNode.children  = currentNode.children || []
    const { children }     = currentNode

    const { endRegexp, content: {end: {offset: offsetEnd, point} }, forward } = mdTags[currentNode.tag]

    while(offset < md.length){
        const [nearest, nearestMatch] = findNearest(md, mdTags, offset)
        const endMatch = md.offsetMatch(offset, endRegexp)
        if (endMatch) {
            if (!nearest || endMatch.index <= nearestMatch.index){
                currentNode.endContent = offset + endMatch.index + offsetEnd + (point === 'end' ? endMatch[0].length : 0)
                children.push(md.slice(offset, currentNode.endContent))
                offset += endMatch.index + forward.offset + (forward.point === 'endEnd' ? endMatch[0].length : 0)
                console.log(currentNode.tag, forward.point === 'endEnd' ? endMatch[0].length : 0)
                currentNode.endOffset = offset
                currentNode.endMatch  = endMatch
                return currentNode
            }
        }
        if (nearest){
            const {begin,content: {start}} = mdTags[nearest]
            if (nearestMatch.index){
                children.push(md.slice(offset, offset + nearestMatch.index + begin))
                offset += nearestMatch.index
            }
            else {
                const newNode = {tag: nearest, startOffset: offset, parent: currentNode, startMatch: nearestMatch}
                children.push(newNode)
                buildAST(md, mdTags, offset + start.offset + (start.point === 'end' ? nearestMatch[0].length : 0), tree, [...stack, newNode])
                offset = newNode.endOffset
            }
        }
        else {
            children.push(md.slice(offset))
            offset = md.length
        }
    }
    return currentNode
}

const md = 
`
# heading1
какой-то _текст_
# heading2
а тут **шо** цикавого?)))
`;
console.log( buildAST(md).children)