package extension

import (
	
	
	

	
	gast 
	
	
	
	
	
	
)

var escapedPipeCellListKey = parser.NewContextKey()

type escapedPipeCell struct {
	Cell        *ast.TableCell
	Pos         []int
	Transformed bool
}
TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format.
TableCellAlignDefault renders alignments by default method. With XHTML, alignments are rendered as an align attribute. With HTML5, alignments are rendered as a style attribute.
TableCellAlignAttribute renders alignments as an align attribute.
TableCellAlignStyle renders alignments as a style attribute.
TableCellAlignNone does not care about alignments. If you using classes or other styles, you can add these attributes in an ASTTransformer.
TableConfig struct holds options for the extension.
type TableConfig struct {
	html.Config
TableCellAlignMethod indicates how are table celss aligned.
TableOption interface is a functional option interface for the extension.
type TableOption interface {
SetTableOption sets given option to the extension.
	SetTableOption(*TableConfig)
}
NewTableConfig returns a new Config with defaults.
SetOption implements renderer.SetOptioner.
func ( *TableConfig) ( renderer.OptionName,  interface{}) {
	switch  {
	case optTableCellAlignMethod:
		.TableCellAlignMethod = .(TableCellAlignMethod)
	default:
		.Config.SetOption(, )
	}
}

type withTableHTMLOptions struct {
	value []html.Option
}

func ( *withTableHTMLOptions) ( *renderer.Config) {
	if .value != nil {
		for ,  := range .value {
			.(renderer.Option).SetConfig()
		}
	}
}

func ( *withTableHTMLOptions) ( *TableConfig) {
	if .value != nil {
		for ,  := range .value {
			.SetHTMLOption(&.Config)
		}
	}
}
WithTableHTMLOptions is functional option that wraps goldmark HTMLRenderer options.
WithTableCellAlignMethod is a functional option that indicates how are table cells aligned in HTML format.
func ( TableCellAlignMethod) TableOption {
	return &withTableCellAlignMethod{}
}

func ( []byte) bool {
	for ,  := range  {
		if !(util.IsSpace() ||  == '-' ||  == '|' ||  == ':') {
			return false
		}
	}
	return true
}

var tableDelimLeft = regexp.MustCompile(`^\s*\:\-+\s*$`)
var tableDelimRight = regexp.MustCompile(`^\s*\-+\:\s*$`)
var tableDelimCenter = regexp.MustCompile(`^\s*\:\-+\:\s*$`)
var tableDelimNone = regexp.MustCompile(`^\s*\-+\s*$`)

type tableParagraphTransformer struct {
}

var defaultTableParagraphTransformer = &tableParagraphTransformer{}
NewTableParagraphTransformer returns a new ParagraphTransformer that can transform paragraphs into tables.
func () parser.ParagraphTransformer {
	return defaultTableParagraphTransformer
}

func ( *tableParagraphTransformer) ( *gast.Paragraph,  text.Reader,  parser.Context) {
	 := .Lines()
	if .Len() < 2 {
		return
	}
	for  := 1;  < .Len(); ++ {
		 := .parseDelimiter(.At(), )
		if  == nil {
			continue
		}
		 := .parseRow(.At(-1), , true, , )
		if  == nil || len() != .ChildCount() {
			return
		}
		 := ast.NewTable()
		.Alignments = 
		.AppendChild(, ast.NewTableHeader())
		for  :=  + 1;  < .Len(); ++ {
			.AppendChild(, .parseRow(.At(), , false, , ))
		}
		.Lines().SetSliced(0, -1)
		.Parent().InsertAfter(.Parent(), , )
		if .Lines().Len() == 0 {
			.Parent().RemoveChild(.Parent(), )
		} else {
			 := .Lines().At( - 2)
			.Stop = .Stop - 1 // trim last newline(\n)
			.Lines().Set(-2, )
		}
	}
}

func ( *tableParagraphTransformer) ( text.Segment,  []ast.Alignment,  bool,  text.Reader,  parser.Context) *ast.TableRow {
	 := .Source()
	 := .Value()
	 := 0
	 += util.TrimLeftSpaceLength()
	 := len()
	 -= util.TrimRightSpaceLength()
	 := ast.NewTableRow()
	if len() > 0 && [] == '|' {
		++
	}
	if len() > 0 && [-1] == '|' {
		--
	}
	 := 0
	for ;  < ; ++ {
		 := ast.AlignNone
		if  >= len() {
			if ! {
				return 
			}
		} else {
			 = []
		}

		var  *escapedPipeCell
		 := ast.NewTableCell()
		.Alignment = 
		 := false
		 := 
		for ;  < ; ++ {
			if [] == '`' {
				 = true
			}
			if [] == '|' {
				if  == 0 || [-1] != '\\' {
					break
				} else if  {
					if  == nil {
						 = &escapedPipeCell{, []int{}, false}
						 := .ComputeIfAbsent(escapedPipeCellListKey,
							func() interface{} {
								return []*escapedPipeCell{}
							}).([]*escapedPipeCell)
						 = append(, )
						.Set(escapedPipeCellListKey, )
					}
					.Pos = append(.Pos, .Start+-1)
				}
			}
		}
		 := text.NewSegment(.Start+, .Start+)
		 = .TrimLeftSpace()
		 = .TrimRightSpace()
		.Lines().Append()
		.AppendChild(, )
		 =  + 1
	}
	for ;  < len(); ++ {
		.AppendChild(, ast.NewTableCell())
	}
	return 
}

func ( *tableParagraphTransformer) ( text.Segment,  text.Reader) []ast.Alignment {
	 := .Value(.Source())
	if !isTableDelim() {
		return nil
	}
	 := bytes.Split(, []byte{'|'})
	if util.IsBlank([0]) {
		 = [1:]
	}
	if len() > 0 && util.IsBlank([len()-1]) {
		 = [:len()-1]
	}

	var  []ast.Alignment
	for ,  := range  {
		if tableDelimLeft.Match() {
			 = append(, ast.AlignLeft)
		} else if tableDelimRight.Match() {
			 = append(, ast.AlignRight)
		} else if tableDelimCenter.Match() {
			 = append(, ast.AlignCenter)
		} else if tableDelimNone.Match() {
			 = append(, ast.AlignNone)
		} else {
			return nil
		}
	}
	return 
}

type tableASTTransformer struct {
}

var defaultTableASTTransformer = &tableASTTransformer{}
NewTableASTTransformer returns a parser.ASTTransformer for tables.
func () parser.ASTTransformer {
	return defaultTableASTTransformer
}

func ( *tableASTTransformer) ( *gast.Document,  text.Reader,  parser.Context) {
	 := .Get(escapedPipeCellListKey)
	if  == nil {
		return
	}
	.Set(escapedPipeCellListKey, nil)
	for ,  := range .([]*escapedPipeCell) {
		if .Transformed {
			continue
		}
		_ = gast.Walk(.Cell, func( gast.Node,  bool) (gast.WalkStatus, error) {
			if ! || .Kind() != gast.KindCodeSpan {
				return gast.WalkContinue, nil
			}

			for  := .FirstChild();  != nil; {
				 := .NextSibling()
				if .Kind() != gast.KindText {
					 = 
					continue
				}
				 := .Parent()
				 := &.(*gast.Text).Segment
				 := 
				for ,  := range .([]*escapedPipeCell) {
					for ,  := range .Pos {
						if .Start <=  &&  < .Stop {
							 := .(*gast.Text).Segment
							 := gast.NewRawTextSegment(.WithStop())
							 := gast.NewRawTextSegment(.WithStart( + 1))
							.InsertAfter(, , )
							.InsertAfter(, , )
							.RemoveChild(, )
							 = 
							.Transformed = true
						}
					}
				}
				 = 
			}
			return gast.WalkContinue, nil
		})
	}
}
TableHTMLRenderer is a renderer.NodeRenderer implementation that renders Table nodes.
NewTableHTMLRenderer returns a new TableHTMLRenderer.
func ( ...TableOption) renderer.NodeRenderer {
	 := &TableHTMLRenderer{
		TableConfig: NewTableConfig(),
	}
	for ,  := range  {
		.SetTableOption(&.TableConfig)
	}
	return 
}
TableAttributeFilter defines attribute names which table elements can have.
var TableAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("align"),       // [Deprecated]
	[]byte("bgcolor"),     // [Deprecated]
	[]byte("border"),      // [Deprecated]
	[]byte("cellpadding"), // [Deprecated]
	[]byte("cellspacing"), // [Deprecated]
	[]byte("frame"),       // [Deprecated]
	[]byte("rules"),       // [Deprecated]
	[]byte("summary"),     // [Deprecated]
	[]byte("width"),       // [Deprecated]
)

func ( *TableHTMLRenderer) ( util.BufWriter,  []byte,  gast.Node,  bool) (gast.WalkStatus, error) {
	if  {
		_, _ = .WriteString("<table")
		if .Attributes() != nil {
			html.RenderAttributes(, , TableAttributeFilter)
		}
		_, _ = .WriteString(">\n")
	} else {
		_, _ = .WriteString("</table>\n")
	}
	return gast.WalkContinue, nil
}
TableHeaderAttributeFilter defines attribute names which <thead> elements can have.
var TableHeaderAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("align"),   // [Deprecated since HTML4] [Obsolete since HTML5]
	[]byte("bgcolor"), // [Not Standardized]
	[]byte("char"),    // [Deprecated since HTML4] [Obsolete since HTML5]
	[]byte("charoff"), // [Deprecated since HTML4] [Obsolete since HTML5]
	[]byte("valign"),  // [Deprecated since HTML4] [Obsolete since HTML5]
)

func ( *TableHTMLRenderer) ( util.BufWriter,  []byte,  gast.Node,  bool) (gast.WalkStatus, error) {
	if  {
		_, _ = .WriteString("<thead")
		if .Attributes() != nil {
			html.RenderAttributes(, , TableHeaderAttributeFilter)
		}
		_, _ = .WriteString(">\n")
		_, _ = .WriteString("<tr>\n") // Header <tr> has no separate handle
	} else {
		_, _ = .WriteString("</tr>\n")
		_, _ = .WriteString("</thead>\n")
		if .NextSibling() != nil {
			_, _ = .WriteString("<tbody>\n")
		}
	}
	return gast.WalkContinue, nil
}
TableRowAttributeFilter defines attribute names which <tr> elements can have.
var TableRowAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("align"),   // [Obsolete since HTML5]
	[]byte("bgcolor"), // [Obsolete since HTML5]
	[]byte("char"),    // [Obsolete since HTML5]
	[]byte("charoff"), // [Obsolete since HTML5]
	[]byte("valign"),  // [Obsolete since HTML5]
)

func ( *TableHTMLRenderer) ( util.BufWriter,  []byte,  gast.Node,  bool) (gast.WalkStatus, error) {
	if  {
		_, _ = .WriteString("<tr")
		if .Attributes() != nil {
			html.RenderAttributes(, , TableRowAttributeFilter)
		}
		_, _ = .WriteString(">\n")
	} else {
		_, _ = .WriteString("</tr>\n")
		if .Parent().LastChild() ==  {
			_, _ = .WriteString("</tbody>\n")
		}
	}
	return gast.WalkContinue, nil
}
TableThCellAttributeFilter defines attribute names which table <th> cells can have.
var TableThCellAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("abbr"), // [OK] Contains a short abbreviated description of the cell's content [NOT OK in <td>]

	[]byte("align"),   // [Obsolete since HTML5]
	[]byte("axis"),    // [Obsolete since HTML5]
	[]byte("bgcolor"), // [Not Standardized]
	[]byte("char"),    // [Obsolete since HTML5]
	[]byte("charoff"), // [Obsolete since HTML5]

	[]byte("colspan"), // [OK] Number of columns that the cell is to span
	[]byte("headers"), // [OK] This attribute contains a list of space-separated strings, each corresponding to the id attribute of the <th> elements that apply to this element

	[]byte("height"), // [Deprecated since HTML4] [Obsolete since HTML5]

	[]byte("rowspan"), // [OK] Number of rows that the cell is to span
	[]byte("scope"),   // [OK] This enumerated attribute defines the cells that the header (defined in the <th>) element relates to [NOT OK in <td>]

	[]byte("valign"), // [Obsolete since HTML5]
	[]byte("width"),  // [Deprecated since HTML4] [Obsolete since HTML5]
)
TableTdCellAttributeFilter defines attribute names which table <td> cells can have.
var TableTdCellAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("abbr"),    // [Obsolete since HTML5] [OK in <th>]
	[]byte("align"),   // [Obsolete since HTML5]
	[]byte("axis"),    // [Obsolete since HTML5]
	[]byte("bgcolor"), // [Not Standardized]
	[]byte("char"),    // [Obsolete since HTML5]
	[]byte("charoff"), // [Obsolete since HTML5]

	[]byte("colspan"), // [OK] Number of columns that the cell is to span
	[]byte("headers"), // [OK] This attribute contains a list of space-separated strings, each corresponding to the id attribute of the <th> elements that apply to this element

	[]byte("height"), // [Deprecated since HTML4] [Obsolete since HTML5]

	[]byte("rowspan"), // [OK] Number of rows that the cell is to span

	[]byte("scope"),  // [Obsolete since HTML5] [OK in <th>]
	[]byte("valign"), // [Obsolete since HTML5]
	[]byte("width"),  // [Deprecated since HTML4] [Obsolete since HTML5]
)

func ( *TableHTMLRenderer) ( util.BufWriter,  []byte,  gast.Node,  bool) (gast.WalkStatus, error) {
	 := .(*ast.TableCell)
	 := "td"
	if .Parent().Kind() == ast.KindTableHeader {
		 = "th"
	}
	if  {
		fmt.Fprintf(, "<%s", )
		if .Alignment != ast.AlignNone {
			 := .TableConfig.TableCellAlignMethod
			if  == TableCellAlignDefault {
				if .Config.XHTML {
					 = TableCellAlignAttribute
				} else {
					 = TableCellAlignStyle
				}
			}
			switch  {
			case TableCellAlignAttribute:
				if ,  := .AttributeString("align"); ! { // Skip align render if overridden
					fmt.Fprintf(, ` align="%s"`, .Alignment.String())
				}
			case TableCellAlignStyle:
				,  := .AttributeString("style")
				var  util.CopyOnWriteBuffer
				if  {
					 = util.NewCopyOnWriteBuffer(.([]byte))
					.AppendByte(';')
				}
				 := fmt.Sprintf("text-align:%s", .Alignment.String())
				.AppendString()
				.SetAttributeString("style", .Bytes())
			}
		}
		if .Attributes() != nil {
			if  == "td" {
				html.RenderAttributes(, , TableTdCellAttributeFilter) // <td>
			} else {
				html.RenderAttributes(, , TableThCellAttributeFilter) // <th>
			}
		}
		_ = .WriteByte('>')
	} else {
		fmt.Fprintf(, "</%s>\n", )
	}
	return gast.WalkContinue, nil
}

type table struct {
	options []TableOption
}
Table is an extension that allow you to use GFM tables .
var Table = &table{
	options: []TableOption{},
}