package parser

import (
	

	
	
	
)

type listItemType int

const (
	notList listItemType = iota
	bulletList
	orderedList
)

var skipListParserKey = NewContextKey()
var emptyListItemWithBlankLines = NewContextKey()
var listItemFlagValue interface{} = true

// Same as
// `^(([ ]*)([\-\*\+]))(\s+.*)?\n?$`.FindSubmatchIndex or
// `^(([ ]*)(\d{1,9}[\.\)]))(\s+.*)?\n?$`.FindSubmatchIndex.
func parseListItem( []byte) ([6]int, listItemType) {
	 := 0
	 := len()
	 := [6]int{}
	for ;  <  && [] == ' '; ++ {
		 := []
		if  == '\t' {
			return , notList
		}
	}
	if  > 3 {
		return , notList
	}
	[0] = 0
	[1] = 
	[2] = 
	var  listItemType
	if  <  && ([] == '-' || [] == '*' || [] == '+') {
		++
		[3] = 
		 = bulletList
	} else if  <  {
		for ;  <  && util.IsNumeric([]); ++ {
		}
		[3] = 
		if [3] == [2] || [3]-[2] > 9 {
			return , notList
		}
		if  <  && ([] == '.' || [] == ')') {
			++
			[3] = 
		} else {
			return , notList
		}
		 = orderedList
	} else {
		return , notList
	}
	if  <  && [] != '\n' {
		,  := util.IndentWidth([:], 0)
		if  == 0 {
			return , notList
		}
	}
	if  >=  {
		[4] = -1
		[5] = -1
		return , 
	}
	[4] = 
	[5] = len()
	if [[5]-1] == '\n' && [] != '\n' {
		[5]--
	}
	return , 
}

func matchesListItem( []byte,  bool) ([6]int, listItemType) {
	,  := parseListItem()
	if  != notList && (! ||  && [1] < 4) {
		return , 
	}
	return , notList
}

func calcListOffset( []byte,  [6]int) int {
	var  int
	if [4] < 0 || util.IsBlank([[4]:]) { // list item starts with a blank line
		 = 1
	} else {
		, _ = util.IndentWidth([[4]:], [4])
		if  > 4 { // offseted codeblock
			 = 1
		}
	}
	return 
}

func lastOffset( ast.Node) int {
	 := .LastChild()
	if  != nil {
		return .(*ast.ListItem).Offset
	}
	return 0
}

type listParser struct {
}

var defaultListParser = &listParser{}

// NewListParser returns a new BlockParser that
// parses lists.
// This parser must take precedence over the ListItemParser.
func () BlockParser {
	return defaultListParser
}

func ( *listParser) () []byte {
	return []byte{'-', '+', '*', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
}

func ( *listParser) ( ast.Node,  text.Reader,  Context) (ast.Node, State) {
	 := .LastOpenedBlock().Node
	if ,  := .(*ast.List);  || .Get(skipListParserKey) != nil {
		.Set(skipListParserKey, nil)
		return nil, NoChildren
	}
	,  := .PeekLine()
	,  := matchesListItem(, true)
	if  == notList {
		return nil, NoChildren
	}
	 := -1
	if  == orderedList {
		 := [[2] : [3]-1]
		, _ = strconv.Atoi(string())
	}

	if ast.IsParagraph() && .Parent() ==  {
		// we allow only lists starting with 1 to interrupt paragraphs.
		if  == orderedList &&  != 1 {
			return nil, NoChildren
		}
		//an empty list item cannot interrupt a paragraph:
		if [4] < 0 || util.IsBlank([[4]:[5]]) {
			return nil, NoChildren
		}
	}

	 := [[3]-1]
	 := ast.NewList()
	if  > -1 {
		.Start = 
	}
	.Set(emptyListItemWithBlankLines, nil)
	return , HasChildren
}

func ( *listParser) ( ast.Node,  text.Reader,  Context) State {
	 := .(*ast.List)
	,  := .PeekLine()
	if util.IsBlank() {
		if .LastChild().ChildCount() == 0 {
			.Set(emptyListItemWithBlankLines, listItemFlagValue)
		}
		return Continue | HasChildren
	}

	// "offset" means a width that bar indicates.
	//    -  aaaaaaaa
	// |----|
	//
	// If the indent is less than the last offset like
	// - a
	//  - b          <--- current line
	// it maybe a new child of the list.
	//
	// Empty list items can have multiple blanklines
	//
	// -             <--- 1st item is an empty thus "offset" is unknown
	//
	//
	//   -           <--- current line
	//
	// -> 1 list with 2 blank items
	//
	// So if the last item is an empty, it maybe a new child of the list.
	//
	 := lastOffset()
	 := .LastChild().ChildCount() == 0
	,  := util.IndentWidth(, .LineOffset())

	if  <  ||  {
		if  < 4 {
			,  := matchesListItem(, false) // may have a leading spaces more than 3
			if  != notList && [1]- < 4 {
				 := [[3]-1]
				if !.CanContinue(,  == orderedList) {
					return Close
				}
				// Thematic Breaks take precedence over lists
				if isThematicBreak([[3]-1:], 0) {
					 := false
					 := .LastOpenedBlock().Node
					if ast.IsParagraph() {
						,  := matchesSetextHeadingBar([[3]-1:])
						if  &&  == '-' {
							 = true
						}
					}
					if ! {
						return Close
					}
				}
				return Continue | HasChildren
			}
		}
		if ! {
			return Close
		}
	}

	if  &&  <  {
		return Close
	}

	// Non empty items can not exist next to an empty list item
	// with blank lines. So we need to close the current list
	//
	// -
	//
	//   foo
	//
	// -> 1 list with 1 blank items and 1 paragraph
	if .Get(emptyListItemWithBlankLines) != nil {
		return Close
	}
	return Continue | HasChildren
}

func ( *listParser) ( ast.Node,  text.Reader,  Context) {
	 := .(*ast.List)

	for  := .FirstChild();  != nil && .IsTight;  = .NextSibling() {
		if .FirstChild() != nil && .FirstChild() != .LastChild() {
			for  := .FirstChild().NextSibling();  != nil;  = .NextSibling() {
				if .HasBlankPreviousLines() {
					.IsTight = false
					break
				}
			}
		}
		if  != .FirstChild() {
			if .HasBlankPreviousLines() {
				.IsTight = false
			}
		}
	}

	if .IsTight {
		for  := .FirstChild();  != nil;  = .NextSibling() {
			for  := .FirstChild();  != nil; {
				,  := .(*ast.Paragraph)
				 = .NextSibling()
				if  {
					 := ast.NewTextBlock()
					.SetLines(.Lines())
					.ReplaceChild(, , )
				}
			}
		}
	}
}

func ( *listParser) () bool {
	return true
}

func ( *listParser) () bool {
	return false
}