// Copyright (c) 2016 Kiel University and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

package components

import (
	

	
)

// The declaration order is the one used by Point.Quadrant in ELK 0.8.1. It
// is observable in RIGHT_SPECIAL_ORDER below.
type hullQuadrant uint8

const (
	hullQ1 hullQuadrant = iota
	hullQ4
	hullQ2
	hullQ3
)

func ( hullQuadrant) () bool {
	return  == hullQ1 ||  == hullQ2
}

func ( hullQuadrant) () bool {
	return  == hullQ1 ||  == hullQ4
}

func hullBothLeftOrRight(,  hullQuadrant) bool {
	return  == hullQ1 &&  == hullQ4 ||
		 == hullQ4 &&  == hullQ1 ||
		 == hullQ3 &&  == hullQ2 ||
		 == hullQ2 &&  == hullQ3
}

func hullOneLeftOneRight(,  hullQuadrant) bool {
	return  == hullQ1 && ( == hullQ2 ||  == hullQ3) ||
		 == hullQ4 && ( == hullQ3 ||  == hullQ2)
}

type hullPoint struct {
	x, y     float64
	quadrant hullQuadrant
	convex   bool
}

func newHullPoint(,  float64) hullPoint {
	return hullPoint{x: , y: , convex: true}
}

func quadrantHullPoint( hullPoint,  hullQuadrant) hullPoint {
	.quadrant = 
	.convex = true
	return 
}

type hullPointOrder func(first, second hullPoint) int

func stableHullSort( []hullPoint,  hullPointOrder) []hullPoint {
	 := append([]hullPoint(nil), ...)
	sort.SliceStable(, func(,  int) bool {
		return ([], []) < 0
	})
	return 
}

func compareFloat(,  float64) int {
	switch {
	case  < :
		return -1
	case  > :
		return 1
	default:
		return 0
	}
}

func rightHighFirst(,  hullPoint) int {
	if .x == .x {
		return compareFloat(.y, .y)
	}
	return compareFloat(.x, .x)
}

func rightLowFirst(,  hullPoint) int {
	if .x == .x {
		return compareFloat(.y, .y)
	}
	return compareFloat(.x, .x)
}

func leftHighFirst(,  hullPoint) int {
	if .x == .x {
		return compareFloat(.y, .y)
	}
	return compareFloat(.x, .x)
}

func leftLowFirst(,  hullPoint) int {
	if .x == .x {
		return compareFloat(.y, .y)
	}
	return compareFloat(.x, .x)
}

func maximalHullElements(
	 []hullPoint,
	 hullQuadrant,
	 hullPointOrder,
) []hullPoint {
	 := stableHullSort(, )
	 := make([]hullPoint, 0, len())
	 :=  == hullQ1 ||  == hullQ2
	var  float64
	if  {
		 = positiveInfinity
	} else {
		 = negativeInfinity
	}
	for ,  := range  {
		if  && .y <  || ! && .y >  {
			 = append(, quadrantHullPoint(, ))
			 = .y
		}
	}
	return 
}

func addConcaveHullCorners( []hullPoint,  hullQuadrant) []hullPoint {
	if len() == 0 {
		return nil
	}
	 := make([]hullPoint, 0, len()*2-1)
	 := [0]
	 = append(, )
	for ,  := range [1:] {
		 = append(, hullPoint{
			x: .x, y: .y, quadrant: , convex: false,
		})
		 = append(, )
		 = 
	}
	return 
}

func reverseHullPoints( []hullPoint) []hullPoint {
	 := make([]hullPoint, len())
	for  := range  {
		[len()-1-] = []
	}
	return 
}

// splitRectilinearHull is a source-shaped port of
// RectilinearConvexHull.of(points).splitIntoRectangles().
func splitRectilinearHull( []hullPoint) []geometry.Rectangle {
	if len() == 0 {
		return nil
	}

	var ,  *hullPoint
	for  := range  {
		 := &[]
		// Equality is intentional: the two extrema may be equal.
		if  == nil || .x <= .x {
			 = 
			 = 
		}
	}

	 := addConcaveHullCorners(
		maximalHullElements(, hullQ1, rightLowFirst), hullQ1)
	 := addConcaveHullCorners(
		maximalHullElements(, hullQ4, rightHighFirst), hullQ4)
	 := addConcaveHullCorners(
		maximalHullElements(, hullQ2, leftLowFirst), hullQ2)
	 := addConcaveHullCorners(
		maximalHullElements(, hullQ3, leftHighFirst), hullQ3)

	 := make([]hullPoint, 0, len()+len()+len()+len())
	 = append(, ...)
	 = append(, reverseHullPoints()...)
	 = append(, ...)
	 = append(, reverseHullPoints()...)

	 := func(,  hullPoint) int {
		if .x != .x {
			return compareFloat(.x, .x)
		}
		if .quadrant == .quadrant ||
			hullBothLeftOrRight(.quadrant, .quadrant) {
			 := -1
			if .quadrant.left() {
				 = 1
			}
			if .convex && !.convex {
				return 
			}
			if !.convex && .convex {
				return -
			}
		}
		return int(.quadrant) - int(.quadrant)
	}
	 = stableHullSort(, )

	 := .x
	if  != nil && .x <  {
		 = .x
	}
	var ,  *hullPoint
	var  *geometry.Rectangle
	var  *hullPoint
	 := make([]geometry.Rectangle, 0)
	for  := range  {
		 := &[]
		if  != nil &&
			(.x != .x ||
				hullOneLeftOneRight(.quadrant, .quadrant)) {
			 = append(, *)
			 = .X + .Width
			 = nil
			 = nil
		}

		if .quadrant.upper() {
			 = 
		} else {
			 = 
		}

		 := .quadrant == hullQ1 && !.convex ||
			.quadrant == hullQ2 && .convex ||
			.quadrant == hullQ3 && .convex ||
			.quadrant == hullQ4 && !.convex
		if  &&  != nil &&  != nil {
			 := geometry.Rectangle{
				X: , Y: .y,
				Width: .x - , Height: .y - .y,
			}
			 = &
			 = 
		}
	}
	if  != nil {
		 = append(, *)
	}
	return 
}