import math
from mathutils import Vector


class BBox:
	@classmethod
	def calc_bbox(cls, coords):
		xmin = math.inf
		xmax = -math.inf
		ymin = math.inf
		ymax = -math.inf

		for x, y in coords:
			if xmin > x:
				xmin = x
			if xmax < x:
				xmax = x
			if ymin > y:
				ymin = y
			if ymax < y:
				ymax = y
		return cls(xmin, xmax, ymin, ymax)

	@classmethod
	def calc_bbox_uv(cls, group, uv_layers, are_loops=False):
		xmin = math.inf
		xmax = -math.inf
		ymin = math.inf
		ymax = -math.inf

		if not are_loops:
			for face in group:
				for loop in face.loops:
					x, y = loop[uv_layers].uv
					if xmin > x:
						xmin = x
					if xmax < x:
						xmax = x
					if ymin > y:
						ymin = y
					if ymax < y:
						ymax = y
		else:
			for loop in group:
				x, y = loop[uv_layers].uv
				if xmin > x:
					xmin = x
				if xmax < x:
					xmax = x
				if ymin > y:
					ymin = y
				if ymax < y:
					ymax = y
		return cls(xmin, xmax, ymin, ymax)

	@classmethod
	def init_from_minmax(cls, min, max):
		bbox = cls(min[0], max[0], min[1], max[1])
		bbox.sanitize()
		return bbox

	def __init__(self, xmin=math.inf, xmax=-math.inf, ymin=math.inf, ymax=-math.inf):
		self.xmin = xmin
		self.xmax = xmax
		self.ymin = ymin
		self.ymax = ymax

	def __str__(self):
		return f"xmin={self.xmin:.6}, xmax={self.xmax:.6}, ymin={self.ymin:.6}, ymax={self.ymax:.6}, width={self.width:.6}, height={self.height:.6}"

	@property
	def is_valid(self) -> bool:
		return (self.xmin < self.xmax) and (self.ymin < self.ymax)

	@property
	def max(self):
		return Vector((self.xmax, self.ymax))

	@property
	def min(self):
		return Vector((self.xmin, self.ymin))

	@property
	def left_upper(self):
		return Vector((self.xmin, self.ymax))

	@property
	def left_bottom(self):
		return Vector((self.xmin, self.ymin))

	@property
	def right_bottom(self):
		return Vector((self.xmax, self.ymin))

	@property
	def right_upper(self):
		return Vector((self.xmax, self.ymax))

	@property
	def upper(self):
		return Vector(((self.xmin + self.xmax) * 0.5, self.ymax))

	@property
	def bottom(self):
		return Vector(((self.xmin + self.xmax) * 0.5, self.ymin))

	@property
	def left(self):
		return Vector((self.xmin, (self.ymin + self.ymax) * 0.5))

	@property
	def right(self):
		return Vector((self.xmax, (self.ymin + self.ymax) * 0.5))

	@property
	def center(self):
		return Vector(((self.xmin + self.xmax) * 0.5, (self.ymin + self.ymax) * 0.5))

	@property
	def width(self) -> float:
		return self.xmax - self.xmin

	@property
	def height(self) -> float:
		return self.ymax - self.ymin

	@property
	def max_lenght(self):
		return max(self.width, self.height)

	@property
	def min_lenght(self):
		return min(self.width, self.height)

	@property
	def half_width(self) -> float:
		return (self.xmax - self.xmin) * 0.5

	@property
	def half_height(self) -> float:
		return (self.ymax - self.ymin) * 0.5

	@property
	def area(self):
		return self.width * self.height

	@property
	def is_empty(self) -> bool:
		return (self.xmax <= self.xmin) or (self.ymax <= self.ymin)

	def union(self, other):
		if self.xmin > other.xmin:
			self.xmin = other.xmin
		if self.xmax < other.xmax:
			self.xmax = other.xmax
		if self.ymin > other.ymin:
			self.ymin = other.ymin
		if self.ymax < other.ymax:
			self.ymax = other.ymax
		return self

	def sanitize(self):
		if self.xmin > self.xmax:
			self.xmin, self.xmax = self.xmax, self.xmin
		if self.ymin > self.ymax:
			self.ymin, self.ymax = self.ymax, self.ymin
		# assert self.is_valid
		return self

	def do_minmax_v(self, xy):
		if xy[0] < self.xmin:
			self.xmin = xy[0]
		if xy[0] > self.xmax:
			self.xmax = xy[0]
		if xy[1] < self.ymin:
			self.ymin = xy[1]
		if xy[1] > self.ymax:
			self.ymax = xy[1]

	def update(self, coords):
		for x, y in coords:
			if x < self.xmin:
				self.xmin = x
			if x > self.xmax:
				self.xmax = x
			if y < self.ymin:
				self.ymin = y
			if y > self.ymax:
				self.ymax = y
