package extension

import (
	

	
	gast 
	
	
	
)

var uncloseCounterKey = parser.NewContextKey()

type unclosedCounter struct {
	Single int
	Double int
}

func ( *unclosedCounter) () {
	.Single = 0
	.Double = 0
}

func ( parser.Context) *unclosedCounter {
	 := .Get(uncloseCounterKey)
	if  == nil {
		 = &unclosedCounter{}
		.Set(uncloseCounterKey, )
	}
	return .(*unclosedCounter)
}
TypographicPunctuation is a key of the punctuations that can be replaced with typographic entities.
LeftSingleQuote is '
RightSingleQuote is '
LeftDoubleQuote is "
RightDoubleQuote is "
EnDash is --
EmDash is ---
Ellipsis is ...
LeftAngleQuote is <<
RightAngleQuote is >>
An TypographerConfig struct is a data structure that holds configuration of the Typographer extension.
type TypographerConfig struct {
	Substitutions [][]byte
}

func () [][]byte {
	 := make([][]byte, typographicPunctuationMax)
	[LeftSingleQuote] = []byte("&lsquo;")
	[RightSingleQuote] = []byte("&rsquo;")
	[LeftDoubleQuote] = []byte("&ldquo;")
	[RightDoubleQuote] = []byte("&rdquo;")
	[EnDash] = []byte("&ndash;")
	[EmDash] = []byte("&mdash;")
	[Ellipsis] = []byte("&hellip;")
	[LeftAngleQuote] = []byte("&laquo;")
	[RightAngleQuote] = []byte("&raquo;")
	[Apostrophe] = []byte("&rsquo;")

	return 
}
SetOption implements SetOptioner.
func ( *TypographerConfig) ( parser.OptionName,  interface{}) {
	switch  {
	case optTypographicSubstitutions:
		.Substitutions = .([][]byte)
	}
}
A TypographerOption interface sets options for the TypographerParser.
type TypographerOption interface {
	parser.Option
	SetTypographerOption(*TypographerConfig)
}

const optTypographicSubstitutions parser.OptionName = "TypographicSubstitutions"
TypographicSubstitutions is a list of the substitutions for the Typographer extension.
WithTypographicSubstitutions is a functional otpion that specify replacement text for punctuations.
func ( map[TypographicPunctuation][]byte) TypographerOption {
	 := newDefaultSubstitutions()
	for ,  := range  {
		[] = 
	}

	return &withTypographicSubstitutions{}
}

type typographerDelimiterProcessor struct {
}

func ( *typographerDelimiterProcessor) ( byte) bool {
	return  == '\'' ||  == '"'
}

func ( *typographerDelimiterProcessor) (,  *parser.Delimiter) bool {
	return .Char == .Char
}

func ( *typographerDelimiterProcessor) ( int) gast.Node {
	return nil
}

var defaultTypographerDelimiterProcessor = &typographerDelimiterProcessor{}

type typographerParser struct {
	TypographerConfig
}
NewTypographerParser return a new InlineParser that parses typographer expressions.
func ( ...TypographerOption) parser.InlineParser {
	 := &typographerParser{
		TypographerConfig: TypographerConfig{
			Substitutions: newDefaultSubstitutions(),
		},
	}
	for ,  := range  {
		.SetTypographerOption(&.TypographerConfig)
	}
	return 
}

func ( *typographerParser) () []byte {
	return []byte{'\'', '"', '-', '.', '<', '>'}
}

func ( *typographerParser) ( gast.Node,  text.Reader,  parser.Context) gast.Node {
	,  := .PeekLine()
	 := [0]
	if len() > 2 {
		if  == '-' {
			if .Substitutions[EmDash] != nil && [1] == '-' && [2] == '-' { // ---
				 := gast.NewString(.Substitutions[EmDash])
				.SetCode(true)
				.Advance(3)
				return 
			}
		} else if  == '.' {
			if .Substitutions[Ellipsis] != nil && [1] == '.' && [2] == '.' { // ...
				 := gast.NewString(.Substitutions[Ellipsis])
				.SetCode(true)
				.Advance(3)
				return 
			}
			return nil
		}
	}
	if len() > 1 {
		if  == '<' {
			if .Substitutions[LeftAngleQuote] != nil && [1] == '<' { // <<
				 := gast.NewString(.Substitutions[LeftAngleQuote])
				.SetCode(true)
				.Advance(2)
				return 
			}
			return nil
		} else if  == '>' {
			if .Substitutions[RightAngleQuote] != nil && [1] == '>' { // >>
				 := gast.NewString(.Substitutions[RightAngleQuote])
				.SetCode(true)
				.Advance(2)
				return 
			}
			return nil
		} else if .Substitutions[EnDash] != nil &&  == '-' && [1] == '-' { // --
			 := gast.NewString(.Substitutions[EnDash])
			.SetCode(true)
			.Advance(2)
			return 
		}
	}
	if  == '\'' ||  == '"' {
		 := .PrecendingCharacter()
		 := parser.ScanDelimiter(, , 1, defaultTypographerDelimiterProcessor)
		if  == nil {
			return nil
		}
		 := getUnclosedCounter()
		if  == '\'' {
Handle decade abbrevations such as '90s
				if .CanOpen && !.CanClose && len() > 3 && util.IsNumeric([1]) && util.IsNumeric([2]) && [3] == 's' {
					 := rune(' ')
					if len() > 4 {
						 = util.ToRune(, 4)
					}
					if len() == 3 || util.IsSpaceRune() || util.IsPunctRune() {
						 := gast.NewString(.Substitutions[Apostrophe])
						.SetCode(true)
						.Advance(1)
						return 
					}
Convert normal apostrophes. This is probably more flexible than necessary but converts any apostrophe in between two alphanumerics.
				if len() > 1 && (unicode.IsDigit() || unicode.IsLetter()) && (unicode.IsLetter(util.ToRune(, 1))) {
					 := gast.NewString(.Substitutions[Apostrophe])
					.SetCode(true)
					.Advance(1)
					return 
				}
			}
			if .Substitutions[LeftSingleQuote] != nil && .CanOpen && !.CanClose {
special cases: Alice's, I'm ,Don't, You'd
				if len() > 1 && ([1] == 's' || [1] == 'm' || [1] == 't' || [1] == 'd') && (len() < 3 || util.IsPunct([2]) || util.IsSpace([2])) {
					 = RightSingleQuote
special cases: I've, I'll, You're
				if len() > 2 && (([1] == 'v' && [2] == 'e') || ([1] == 'l' && [2] == 'l') || ([1] == 'r' && [2] == 'e')) && (len() < 4 || util.IsPunct([3]) || util.IsSpace([3])) {
					 = RightSingleQuote
				}
				if  == LeftSingleQuote {
					.Single++
				}

				 := gast.NewString(.Substitutions[])
				.SetCode(true)
				.Advance(1)
				return 
			}
			if .Substitutions[RightSingleQuote] != nil && .Single > 0 {
				 := .CanClose && !.CanOpen
				 := .CanClose && .CanOpen && len() > 1 && ([1] == ',' || [1] == '.' || [1] == '!' || [1] == '?') && (len() == 2 || (len() > 2 && util.IsPunct([2]) || util.IsSpace([2])))
				if  ||  {
					 := gast.NewString(.Substitutions[RightSingleQuote])
					.SetCode(true)
					.Advance(1)
					.Single--
					return 
				}
			}
		}
		if  == '"' {
			if .Substitutions[LeftDoubleQuote] != nil && .CanOpen && !.CanClose {
				 := gast.NewString(.Substitutions[LeftDoubleQuote])
				.SetCode(true)
				.Advance(1)
				.Double++
				return 
			}
			if .Substitutions[RightDoubleQuote] != nil && .Double > 0 {
				 := .CanClose && !.CanOpen
				 := .CanClose && .CanOpen && len() > 1 && ([1] == ',' || [1] == '.' || [1] == '!' || [1] == '?') && (len() == 2 || (len() > 2 && util.IsPunct([2]) || util.IsSpace([2])))
special case: "Monitor 21""
					if len() > 1 && [1] == '"' && unicode.IsDigit() {
						return nil
					}
					 := gast.NewString(.Substitutions[RightDoubleQuote])
					.SetCode(true)
					.Advance(1)
					.Double--
					return 
				}
			}
		}
	}
	return nil
}

func ( *typographerParser) ( gast.Node,  parser.Context) {
	getUnclosedCounter().Reset()
}

type typographer struct {
	options []TypographerOption
}
Typographer is an extension that replaces punctuations with typographic entities.
NewTypographer returns a new Extender that replaces punctuations with typographic entities.