package extension

import (
	
	

	
	
	
	
	
)

var wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]+(?:[/#?][-a-zA-Z0-9@:%_\+.~#!?&/=\(\);,'">\^{}\[\]` + "`" + `]*)?`) //nolint:golint,lll

var urlRegexp = regexp.MustCompile(`^(?:http|https|ftp)://[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]+(?::\d+)?(?:[/#?][-a-zA-Z0-9@:%_+.~#$!?&/=\(\);,'">\^{}\[\]` + "`" + `]*)?`) //nolint:golint,lll

// An LinkifyConfig struct is a data structure that holds configuration of the
// Linkify extension.
type LinkifyConfig struct {
	AllowedProtocols [][]byte
	URLRegexp        *regexp.Regexp
	WWWRegexp        *regexp.Regexp
	EmailRegexp      *regexp.Regexp
}

const (
	optLinkifyAllowedProtocols parser.OptionName = "LinkifyAllowedProtocols"
	optLinkifyURLRegexp        parser.OptionName = "LinkifyURLRegexp"
	optLinkifyWWWRegexp        parser.OptionName = "LinkifyWWWRegexp"
	optLinkifyEmailRegexp      parser.OptionName = "LinkifyEmailRegexp"
)

// SetOption implements SetOptioner.
func ( *LinkifyConfig) ( parser.OptionName,  interface{}) {
	switch  {
	case optLinkifyAllowedProtocols:
		.AllowedProtocols = .([][]byte)
	case optLinkifyURLRegexp:
		.URLRegexp = .(*regexp.Regexp)
	case optLinkifyWWWRegexp:
		.WWWRegexp = .(*regexp.Regexp)
	case optLinkifyEmailRegexp:
		.EmailRegexp = .(*regexp.Regexp)
	}
}

// A LinkifyOption interface sets options for the LinkifyOption.
type LinkifyOption interface {
	parser.Option
	SetLinkifyOption(*LinkifyConfig)
}

type withLinkifyAllowedProtocols struct {
	value [][]byte
}

func ( *withLinkifyAllowedProtocols) ( *parser.Config) {
	.Options[optLinkifyAllowedProtocols] = .value
}

func ( *withLinkifyAllowedProtocols) ( *LinkifyConfig) {
	.AllowedProtocols = .value
}

// WithLinkifyAllowedProtocols is a functional option that specify allowed
// protocols in autolinks. Each protocol must end with ':' like
// 'http:' .
func [ []byte | string]( []) LinkifyOption {
	 := &withLinkifyAllowedProtocols{}
	for ,  := range  {
		.value = append(.value, []byte())
	}
	return 
}

type withLinkifyURLRegexp struct {
	value *regexp.Regexp
}

func ( *withLinkifyURLRegexp) ( *parser.Config) {
	.Options[optLinkifyURLRegexp] = .value
}

func ( *withLinkifyURLRegexp) ( *LinkifyConfig) {
	.URLRegexp = .value
}

// WithLinkifyURLRegexp is a functional option that specify
// a pattern of the URL including a protocol.
func ( *regexp.Regexp) LinkifyOption {
	return &withLinkifyURLRegexp{
		value: ,
	}
}

type withLinkifyWWWRegexp struct {
	value *regexp.Regexp
}

func ( *withLinkifyWWWRegexp) ( *parser.Config) {
	.Options[optLinkifyWWWRegexp] = .value
}

func ( *withLinkifyWWWRegexp) ( *LinkifyConfig) {
	.WWWRegexp = .value
}

// WithLinkifyWWWRegexp is a functional option that specify
// a pattern of the URL without a protocol.
// This pattern must start with 'www.' .
func ( *regexp.Regexp) LinkifyOption {
	return &withLinkifyWWWRegexp{
		value: ,
	}
}

type withLinkifyEmailRegexp struct {
	value *regexp.Regexp
}

func ( *withLinkifyEmailRegexp) ( *parser.Config) {
	.Options[optLinkifyEmailRegexp] = .value
}

func ( *withLinkifyEmailRegexp) ( *LinkifyConfig) {
	.EmailRegexp = .value
}

// WithLinkifyEmailRegexp is a functional otpion that specify
// a pattern of the email address.
func ( *regexp.Regexp) LinkifyOption {
	return &withLinkifyEmailRegexp{
		value: ,
	}
}

type linkifyParser struct {
	LinkifyConfig
}

// NewLinkifyParser return a new InlineParser can parse
// text that seems like a URL.
func ( ...LinkifyOption) parser.InlineParser {
	 := &linkifyParser{
		LinkifyConfig: LinkifyConfig{
			AllowedProtocols: nil,
			URLRegexp:        urlRegexp,
			WWWRegexp:        wwwURLRegxp,
		},
	}
	for ,  := range  {
		.SetLinkifyOption(&.LinkifyConfig)
	}
	return 
}

func ( *linkifyParser) () []byte {
	// ' ' indicates any white spaces and a line head
	return []byte{' ', '*', '_', '~', '('}
}

var (
	protoHTTP  = []byte("http:")
	protoHTTPS = []byte("https:")
	protoFTP   = []byte("ftp:")
	domainWWW  = []byte("www.")
)

func ( *linkifyParser) ( ast.Node,  text.Reader,  parser.Context) ast.Node {
	if .IsInLinkLabel() {
		return nil
	}
	,  := .PeekLine()
	 := 0
	 := .Start
	 := [0]
	// advance if current position is not a line head.
	if  == ' ' ||  == '*' ||  == '_' ||  == '~' ||  == '(' {
		++
		++
		 = [1:]
	}

	var  []int
	var  []byte
	var  ast.AutoLinkType = ast.AutoLinkURL
	if .LinkifyConfig.AllowedProtocols == nil {
		if bytes.HasPrefix(, protoHTTP) || bytes.HasPrefix(, protoHTTPS) || bytes.HasPrefix(, protoFTP) {
			 = .LinkifyConfig.URLRegexp.FindSubmatchIndex()
		}
	} else {
		for ,  := range .LinkifyConfig.AllowedProtocols {
			if bytes.HasPrefix(, ) {
				 = .LinkifyConfig.URLRegexp.FindSubmatchIndex()
				break
			}
		}
	}
	if  == nil && bytes.HasPrefix(, domainWWW) {
		 = .LinkifyConfig.WWWRegexp.FindSubmatchIndex()
		 = []byte("http")
	}
	if  != nil && [0] != 0 {
		 = nil
	}
	if  != nil && [0] == 0 {
		 := [[1]-1]
		if  == '.' {
			[1]--
		} else if  == ')' {
			 := 0
			for  := [1] - 1;  >= [0]; -- {
				if [] == ')' {
					++
				} else if [] == '(' {
					--
				}
			}
			if  > 0 {
				[1] -= 
			}
		} else if  == ';' {
			 := [1] - 2
			for ;  >= [0]; -- {
				if util.IsAlphaNumeric([]) {
					continue
				}
				break
			}
			if  != [1]-2 {
				if [] == '&' {
					[1] -= [1] - 
				}
			}
		}
	}
	if  == nil {
		if len() > 0 && util.IsPunct([0]) {
			return nil
		}
		 = ast.AutoLinkEmail
		 := -1
		if .LinkifyConfig.EmailRegexp == nil {
			 = util.FindEmailIndex()
		} else {
			 := .LinkifyConfig.EmailRegexp.FindSubmatchIndex()
			if  != nil && [0] == 0 {
				 = [1]
			}
		}
		if  < 0 {
			return nil
		}
		 := bytes.IndexByte(, '@')
		 = []int{0, , ,  - 1}
		if  == nil || bytes.IndexByte([[2]:[3]], '.') < 0 {
			return nil
		}
		 := [[1]-1]
		if  == '.' {
			[1]--
		}
		if [1] < len() {
			 := [[1]]
			if  == '-' ||  == '_' {
				return nil
			}
		}
	}
	if  == nil {
		return nil
	}
	if  != 0 {
		 := .WithStop(.Start + 1)
		ast.MergeOrAppendTextSegment(, )
	}
	 := [1] - 1
	for ;  > 0; -- {
		 := []
		switch  {
		case '?', '!', '.', ',', ':', '*', '_', '~':
		default:
			goto 
		}
	}
:
	++
	 += 
	.Advance()
	 := ast.NewTextSegment(text.NewSegment(, +))
	 := ast.NewAutoLink(, )
	.Protocol = 
	return 
}

func ( *linkifyParser) ( ast.Node,  parser.Context) {
	// nothing to do
}

type linkify struct {
	options []LinkifyOption
}

// Linkify is an extension that allow you to parse text that seems like a URL.
var Linkify = &linkify{}

// NewLinkify creates a new [goldmark.Extender] that
// allow you to parse text that seems like a URL.
func ( ...LinkifyOption) goldmark.Extender {
	return &linkify{
		options: ,
	}
}

func ( *linkify) ( goldmark.Markdown) {
	.Parser().AddOptions(
		parser.WithInlineParsers(
			util.Prioritized(NewLinkifyParser(.options...), 999),
		),
	)
}