package geo

import (
	
	
	
	
)

//nolint:forbidigo

type RelativePoint struct {
	XPercentage float64
	YPercentage float64
}

func (,  float64) *RelativePoint {
	// 3 decimal points of precision is enough. Floating points on Bezier curves can reach the level of precision where different machines round differently.
	return &RelativePoint{
		XPercentage: TruncateDecimals(),
		YPercentage: TruncateDecimals(),
	}
}

type Point struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

func (,  float64) *Point {
	return &Point{X: , Y: }
}

func ( *Point) ( *Point) bool {
	if  == nil {
		return  == nil
	} else if  == nil {
		return false
	}
	return (.X == .X) && (.Y == .Y)
}

func ( *Point) ( *Point) int {
	 := Sign(.X - .X)
	if  == 0 {
		return Sign(.Y - .Y)
	}
	return 
}

func ( *Point) () *Point {
	return &Point{X: .X, Y: .Y}
}

type Points []*Point

func ( Points) ( Points) bool {
	if  == nil {
		return  == nil
	} else if  == nil {
		return false
	}
	 := make(map[Point]struct{})
	for ,  := range  {
		[*] = struct{}{}
	}

	for ,  := range  {
		if ,  := [*]; ! {
			return false
		}
	}
	return true
}

func ( Points) () *Point {
	 := make([]float64, 0)
	 := make([]float64, 0)

	for ,  := range  {
		 = append(, .X)
		 = append(, .Y)
	}

	sort.Float64s()
	sort.Float64s()

	 := len() / 2

	 := []
	 := []

	if len()%2 == 0 {
		 += [-1]
		 /= 2
		 += [-1]
		 /= 2
	}

	return &Point{X: , Y: }
}

// GetOrientation gets orientation of pFrom to pTo
// E.g. pFrom ---> pTo, here, pFrom is to the left of pTo, so Left would be returned
func ( *Point) ( *Point) Orientation {
	if .Y < .Y {
		if .X < .X {
			return TopLeft
		}
		if .X > .X {
			return TopRight
		}
		return Top
	}

	if .Y > .Y {
		if .X < .X {
			return BottomLeft
		}
		if .X > .X {
			return BottomRight
		}
		return Bottom
	}

	if .X < .X {
		return Left
	}

	if .X > .X {
		return Right
	}

	return NONE
}

func ( *Point) () string {
	if  == nil {
		return ""
	}
	return fmt.Sprintf("(%v, %v)", .X, .Y)
}

func ( Points) () string {
	 := make([]string, 0, len())
	for ,  := range  {
		 = append(, .ToString())
	}
	return strings.Join(, ", ")
}

// https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
func ( *Point) (,  *Point) float64 {
	 := .X - .X
	 := .Y - .Y
	 := .X - .X
	 := .Y - .Y

	 := ( * ) + ( * )
	 := ( * ) + ( * )

	 := -1.0

	if  != 0 {
		 =  / 
	}

	var  float64
	var  float64

	if  < 0.0 {
		 = .X
		 = .Y
	} else if  > 1.0 {
		 = .X
		 = .Y
	} else {
		 = .X + ( * )
		 = .Y + ( * )
	}

	 := .X - 
	 := .Y - 

	return math.Sqrt(( * ) + ( * ))
}

// Moves the given point by Vector
func ( *Point) ( Vector) *Point {
	return .ToVector().Add().ToPoint()
}

// Creates a Vector of the size between start and endpoint, pointing to endpoint
func ( *Point) ( *Point) Vector {
	return .ToVector().Minus(.ToVector())
}

func ( *Point) () string {
	return fmt.Sprintf("%d,%d", int(.X), int(.Y))
}

// returns true if point p is on orthogonal segment between points a and b
func ( *Point) (,  *Point) bool {
	if .X < .X {
		if .X < .X || .X < .X {
			return false
		}
	} else if .X < .X || .X < .X {
		return false
	}
	if .Y < .Y {
		if .Y < .Y || .Y < .Y {
			return false
		}
	} else if .Y < .Y || .Y < .Y {
		return false
	}
	return true
}

// Creates a Vector pointing to point
func ( *Point) () Vector {
	return []float64{.X, .Y}
}

// get the point of intersection between line segments u and v (or nil if they do not intersect)
func (, , ,  *Point) *Point {
	// https://en.wikipedia.org/wiki/Intersection_(Euclidean_geometry)
	//
	// Example ('-' = 1, '|' = 1):
	//    v0
	//    |
	//u0 -+--- u1
	//    |
	//    |
	//    v1
	//
	// s = 0.2 (1/5 along u)
	// t = 0.25 (1/4 along v)
	// we compute s and t and if they are both in range [0,1], then
	// they intersect and we compute the point of intersection to return

	// when s = 0, x = u.Start.X; when s = 1, x = u.End.X
	// x = s * u1.X + (1 - s) * u0.X
	//   = u0.X + s * (u1.X - u0.X)

	// x = u0.X + s * (u1.X - u0.X)
	//   = v0.X + t * (v1.X - v0.X)
	// y = u0.Y + s * (u1.Y - u0.Y)
	//   = v0.Y + t * (v1.Y - v0.Y)

	// s * (u1.X - u0.X) - t * (v1.X - v0.X) = v0.X - u0.X
	// s*udx - t*vdx = uvdx
	// s*udy - t*vdy = uvdy
	 := .X - .X
	 := .X - .X
	 := .X - .X
	 := .Y - .Y
	 := .Y - .Y
	 := .Y - .Y

	 := (* - *)
	if  == 0 {
		// lines are parallel
		return nil
	}
	// Cramer's rule
	 := (* - *) / 
	 := (* - *) / 

	// lines don't intersect within segments
	if  < 0 ||  > 1 ||  < 0 ||  > 1 {
		// if s or t is outside [0, 1], the intersection of the lines are not on the segments
		return nil
	}

	// use s parameter to get point along u
	 := new(Point)
	.X = .X + math.Round(*)
	.Y = .Y + math.Round(*)
	return 
}

func ( *Point) () {
	if  == nil {
		return
	}
	.X, .Y = .Y, .X
}

// point t% of the way between a and b
func ( *Point) ( *Point,  float64) *Point {
	return NewPoint(
		.X*(1.0-)+.X*,
		.Y*(1.0-)+.Y*,
	)
}

func ( *Point) () {
	.X = float64(float32(.X))
	.Y = float64(float32(.Y))
}

func ( *Point) () {
	.X = TruncateDecimals(.X)
	.Y = TruncateDecimals(.Y)
}

// RemovePoints returns a new Points slice without the points in toRemove
func ( Points,  []bool) Points {
	 := len()
	for ,  := range  {
		if  {
			--
		}
	}

	 := make([]*Point, 0, )
	for  := 0;  < len(); ++ {
		if [] {
			continue
		}
		 = append(, [])
	}
	return 
}