// SPDX-License-Identifier: MIT
// Derived from Rough.js 4.6.6, Copyright (c) 2019 Preet Shihn.

package rough

import (
	
	
	
	
)

type pathToken struct {
	type_ int
	text  string
}

const (
	pathTokenCommand = iota
	pathTokenNumber
	pathTokenEOD
)

var (
	pathWhitespaceRE = regexp.MustCompile(`^[ \t\r\n,]+`)
	pathCommandRE    = regexp.MustCompile(`^[aAcChHlLmMqQsStTvVzZ]`)
	pathNumberRE     = regexp.MustCompile(`^[-+]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][-+]?[0-9]+)?`)
)

func isPathTokenType( pathToken,  int) bool {
	return .type_ == 
}

type pathSegment struct {
	key   string
	data  []float64
	point *Point
}

var pathParams = map[string]int{
	"A": 7,
	"a": 7,
	"C": 6,
	"c": 6,
	"H": 1,
	"h": 1,
	"L": 2,
	"l": 2,
	"M": 2,
	"m": 2,
	"Q": 4,
	"q": 4,
	"S": 4,
	"s": 4,
	"T": 2,
	"t": 2,
	"V": 1,
	"v": 1,
	"Z": 0,
	"z": 0,
}

type parsedPath struct {
	closedSet bool
	closed_   bool
	segments  []pathSegment
}

func newParsedPath( string) *parsedPath {
	 := &parsedPath{}
	.parseData()
	.processPoints()
	return 
}

// tokenize uses independent local matches rather than deprecated regular
// expression statics. Its command arities match path-data-parser 0.1.0.
func ( *parsedPath) ( string) []pathToken {
	 := make([]pathToken, 0)
	for  != "" {
		 := pathWhitespaceRE.FindString()
		 := pathCommandRE.FindString()
		 := pathNumberRE.FindString()
		switch {
		case  != "":
			 = [len():]
		case  != "":
			 = append(, pathToken{type_: pathTokenCommand, text: })
			 = [len():]
		case  != "":
			// The regular expression already establishes the grammar. Like
			// parseFloat, accept range overflow/underflow and keep its Inf/zero.
			,  := strconv.ParseFloat(, 64)
			// The bundle stores `${parseFloat(match)}` and later applies unary
			// plus. This also turns a parsed negative zero into positive zero.
			 = append(, pathToken{type_: pathTokenNumber, text: jsNumberString()})
			 = [len():]
		default:
			return nil
		}
	}
	return append(, pathToken{type_: pathTokenEOD, text: ""})
}

func ( *parsedPath) ( string) {
	 := .tokenize()
	if len() == 0 {
		.segments = nil
		return
	}
	 := 0
	 := []
	 := "BOD"
	.segments = make([]pathSegment, 0)
	for !isPathTokenType(, pathTokenEOD) {
		 := 0
		 := make([]float64, 0)
		if  == "BOD" {
			if .text == "M" || .text == "m" {
				++
				 = pathParams[.text]
				 = .text
			} else {
				.("M0,0" + )
				return
			}
		} else if isPathTokenType(, pathTokenNumber) {
			 = pathParams[]
		} else {
			++
			var  bool
			,  = pathParams[.text]
			if ! {
				return
			}
			 = .text
		}

		if + < len() {
			for  := ;  < +; ++ {
				 := []
				if !isPathTokenType(, pathTokenNumber) {
					return
				}
				,  := strconv.ParseFloat(.text, 64)
				 = append(, )
			}
			if ,  := pathParams[]; ! {
				return
			}
			.segments = append(.segments, pathSegment{key: , data: })
			 += 
			 = []
			if  == "M" {
				 = "L"
			}
			if  == "m" {
				 = "l"
			}
		} else {
			// Rough.js logs and then loops forever for truncated parameter
			// groups. Returning retains the useful parsed prefix without
			// introducing a non-terminating Go API.
			return
		}
	}
}

func ( *parsedPath) () bool {
	if !.closedSet {
		.closed_ = false
		for ,  := range .segments {
			if strings.ToLower(.key) == "z" {
				.closed_ = true
			}
		}
		.closedSet = true
	}
	return .closed_
}

func pointPtr( Point) *Point {
	 := 
	return &
}

func ( *parsedPath) () {
	var  *Point
	 := Point{0, 0}
	for  := range .segments {
		 := &.segments[]
		switch .key {
		case "M", "L", "T":
			.point = pointPtr(Point{.data[0], .data[1]})
		case "m", "l", "t":
			.point = pointPtr(Point{.data[0] + [0], .data[1] + [1]})
		case "H":
			.point = pointPtr(Point{.data[0], [1]})
		case "h":
			.point = pointPtr(Point{.data[0] + [0], [1]})
		case "V":
			.point = pointPtr(Point{[0], .data[0]})
		case "v":
			.point = pointPtr(Point{[0], .data[0] + [1]})
		case "z", "Z":
			if  != nil {
				.point = pointPtr(*)
			}
		case "C":
			.point = pointPtr(Point{.data[4], .data[5]})
		case "c":
			.point = pointPtr(Point{.data[4] + [0], .data[5] + [1]})
		case "S":
			.point = pointPtr(Point{.data[2], .data[3]})
		case "s":
			.point = pointPtr(Point{.data[2] + [0], .data[3] + [1]})
		case "Q":
			.point = pointPtr(Point{.data[2], .data[3]})
		case "q":
			.point = pointPtr(Point{.data[2] + [0], .data[3] + [1]})
		case "A":
			.point = pointPtr(Point{.data[5], .data[6]})
		case "a":
			.point = pointPtr(Point{.data[5] + [0], .data[6] + [1]})
		}

		if .key == "m" || .key == "M" {
			 = nil
		}
		if .point != nil {
			 = *.point
			if  == nil {
				 = pointPtr(*.point)
			}
		}
		if .key == "z" || .key == "Z" {
			 = nil
		}
	}
}

type roughPath struct {
	parsed                *parsedPath
	position              Point
	first                 *Point
	linearPoints_         [][]Point
	linearPointsSet       bool
	bezierReflectionPoint *Point
	quadReflectionPoint   *Point
}

func newRoughPath( string) *roughPath {
	return &roughPath{parsed: newParsedPath()}
}

func ( *roughPath) () []pathSegment {
	return .parsed.segments
}

func ( *roughPath) () bool {
	return .parsed.closed()
}

func ( *roughPath) () [][]Point {
	if !.linearPointsSet {
		 := make([][]Point, 0)
		 := make([]Point, 0)
		for ,  := range .parsed.segments {
			 := strings.ToLower(.key)
			if  == "m" ||  == "z" {
				if len() != 0 {
					 = append(, )
					 = make([]Point, 0)
				}
				if  == "z" {
					continue
				}
			}
			if .point != nil {
				 = append(, *.point)
			}
		}
		if len() != 0 {
			 = append(, )
		}
		.linearPoints_ = 
		.linearPointsSet = true
	}
	return .linearPoints_
}

func ( *roughPath) (,  float64) {
	.position = Point{, }
	if .first == nil {
		.first = pointPtr(.position)
	}
}

func ( *roughPath) () float64 { return .position[0] }

func ( *roughPath) () float64 { return .position[1] }

type roughArcSegment struct {
	cp1 Point
	cp2 Point
	to  Point
}

// roughArcConverter implements the SVG implementation-note conversion used by
// Rough.js to approximate elliptical arcs with cubic Bezier segments.
type roughArcConverter struct {
	segIndex float64
	numSegs  float64
	rx       float64
	ry       float64
	sinPhi   float64
	cosPhi   float64
	center   Point
	theta    float64
	delta    float64
	t        float64
	from     Point
}

func newRoughArcConverter(, ,  Point,  float64, ,  bool) *roughArcConverter {
	 := &roughArcConverter{from: }
	if [0] == [0] && [1] == [1] {
		return 
	}
	 := math.Pi / 180
	.rx = math.Abs([0])
	.ry = math.Abs([1])
	.sinPhi = math.Sin( * )
	.cosPhi = math.Cos( * )
	 := .cosPhi*([0]-[0])/2.0 + .sinPhi*([1]-[1])/2.0
	 := -.sinPhi*([0]-[0])/2.0 + .cosPhi*([1]-[1])/2.0
	 := 0.0
	 := .rx*.rx*.ry*.ry - .rx*.rx** - .ry*.ry**
	if  < 0 {
		 := math.Sqrt(1 - /(.rx*.rx*.ry*.ry))
		.rx *= 
		.ry *= 
		 = 0
	} else {
		 := 1.0
		if  ==  {
			 = -1
		}
		 =  * math.Sqrt(/(.rx*.rx**+.ry*.ry**))
	}
	 :=  * .rx *  / .ry
	 := - * .ry *  / .rx
	.center = Point{
		.cosPhi* - .sinPhi* + ([0]+[0])/2.0,
		.sinPhi* + .cosPhi* + ([1]+[1])/2.0,
	}
	.theta = .calculateVectorAngle(1.0, 0.0, (-)/.rx, (-)/.ry)
	 := .calculateVectorAngle(
		(-)/.rx,
		(-)/.ry,
		(--)/.rx,
		(--)/.ry,
	)
	if ! &&  > 0 {
		 -= 2 * math.Pi
	} else if  &&  < 0 {
		 += 2 * math.Pi
	}
	.numSegs = math.Ceil(math.Abs( / (math.Pi / 2)))
	.delta =  / .numSegs
	.t = (8.0 / 3.0) * math.Sin(.delta/4) * math.Sin(.delta/4) / math.Sin(.delta/2)
	return 
}

func ( *roughArcConverter) () *roughArcSegment {
	if .segIndex == .numSegs {
		return nil
	}
	 := math.Cos(.theta)
	 := math.Sin(.theta)
	 := .theta + .delta
	 := math.Cos()
	 := math.Sin()

	 := Point{
		.cosPhi*.rx* - .sinPhi*.ry* + .center[0],
		.sinPhi*.rx* + .cosPhi*.ry* + .center[1],
	}
	 := Point{
		.from[0] + .t*(-.cosPhi*.rx*-.sinPhi*.ry*),
		.from[1] + .t*(-.sinPhi*.rx*+.cosPhi*.ry*),
	}
	 := Point{
		[0] + .t*(.cosPhi*.rx*+.sinPhi*.ry*),
		[1] + .t*(.sinPhi*.rx*-.cosPhi*.ry*),
	}

	.theta = 
	.from = Point{[0], [1]}
	.segIndex++
	return &roughArcSegment{cp1: , cp2: , to: }
}

func ( *roughArcConverter) (, , ,  float64) float64 {
	 := math.Atan2(, )
	 := math.Atan2(, )
	if  >=  {
		return  - 
	}
	return 2*math.Pi - ( - )
}

type pathFitter struct {
	sets   [][]Point
	closed bool
}

func newPathFitter( [][]Point,  bool) *pathFitter {
	return &pathFitter{sets: , closed: }
}

func ( *pathFitter) ( float64) string {
	 := make([][]Point, 0)
	for ,  := range .sets {
		 := len()
		 := math.Floor( * float64())
		if  < 5 {
			if  <= 5 {
				continue
			}
			 = 5
		}
		 = append(, .reduce(, ))
	}

	var  strings.Builder
	for ,  := range  {
		for ,  := range  {
			if  == 0 {
				.WriteByte('M')
			} else {
				.WriteByte('L')
			}
			.WriteString(jsNumberString([0]))
			.WriteByte(',')
			.WriteString(jsNumberString([1]))
		}
		if .closed {
			.WriteString("z ")
		}
	}
	return .String()
}

func sourceLineLength( Line) float64 {
	return math.Sqrt(math.Pow([0][0]-[1][0], 2) + math.Pow([0][1]-[1][1], 2))
}

func ( *pathFitter) ( []Point,  float64) []Point {
	if float64(len()) <=  {
		return 
	}
	 := append([]Point(nil), ...)
	for float64(len()) >  {
		 := make([]float64, 0, len()-2)
		 := -1.0
		 := -1
		for  := 1;  < len()-1; ++ {
			 := sourceLineLength(Line{[-1], []})
			 := sourceLineLength(Line{[], [+1]})
			 := sourceLineLength(Line{[-1], [+1]})
			 := ( +  + ) / 2.0
			 := math.Sqrt( * ( - ) * ( - ) * ( - ))
			 = append(, )
			if  < 0 ||  <  {
				 = 
				 = 
			}
		}
		_ = 
		if  > 0 {
			copy([:], [+1:])
			 = [:len()-1]
		} else {
			break
		}
	}
	return 
}