"""Base class."""
from __future__ import annotations

from typing import Any

from PIL import Image

from .blend import BlendType

# pylint: disable=too-few-public-methods
# pylint: disable=too-many-arguments


class LayerGroup:
	"""A representation of an image layer or group."""

	def __init__(
		self,
		name: str,
		dimensions: tuple[int, int],
		offsets: tuple[int, int] = (0, 0),
		opacity: float = 1.0,
		visible: bool = True,
		blendmode: BlendType = BlendType.NORMAL,
		**kwargs: Any,
	):
		"""Represent an image layer or group.

		Args:
			name (str): Name of the layer or group
			dimensions ((int, int)): A tuple representing the dimensions in
			pixels
			offsets (tuple, optional): A tuple representing the left and top
			offsets in pixels. Defaults to (0, 0).
			opacity (float, optional): A float representing the alpha value
			where 0 is invisible and 1 is fully visible. Defaults to 1.0.
			visible (bool, optional): Is the layer visible to the user (this
			is often configured per layer or per group by an 'eye' icon).
			Defaults to True.
			blendmode (Blendtype): The blending mode to use. Defaults to BlendType.NORMAL
		"""
		self.name = name
		self.offsets = offsets
		self.opacity = opacity
		self.visible = visible
		self.dimensions = dimensions
		self.blendmode = blendmode
		self.extras = kwargs

	def __repr__(self):
		"""Get the string representation."""
		return self.__str__()

	def __str__(self):
		"""Get the string representation."""
		return (
			"<LayeredImage "
			+ ' "'
			+ self.name
			+ '" ('
			+ str(self.dimensions[0])
			+ "x"
			+ str(self.dimensions[1])
			+ ")>"
		)

	def json(self) -> dict[str, Any]:
		"""Get the object as a dict."""
		return {
			"name": self.name,
			"offsets": self.offsets,
			"opacity": self.opacity,
			"visible": self.visible,
			"dimensions": self.dimensions,
			"blendmode": self.blendmode.name,
		}


class Layer(LayerGroup):
	"""A representation of an image layer."""

	def __init__(
		self,
		name: str,
		image: Image.Image,
		dimensions: tuple[int, int] | None = None,
		offsets: tuple[int, int] = (0, 0),
		opacity: float = 1.0,
		visible: bool = True,
		blendmode: BlendType = BlendType.NORMAL,
	):
		"""Representation of an image layer.

		Args:
			name (str): Name of the layer or group
			image (Image.Image): A PIL Image
			dimensions (tuple[int, int]): A tuple representing the dimensions in
			pixels
			offsets (tuple[int, int], optional): A tuple representing the left and top
			offsets in pixels. Defaults to (0, 0).
			opacity (float, optional): A float representing the alpha value
			where 0 is invisible and 1 is fully visible. Defaults to 1.0.
			visible (bool, optional): Is the layer visible to the user (this
			is often configured per layer or per group by an 'eye' icon).
			Defaults to True.
			blendmode (Blendtype): The blending mode to use. Defaults to BlendType.NORMAL
		"""
		super().__init__(
			name, (0, 0), offsets=offsets, opacity=opacity, visible=visible, blendmode=blendmode
		)
		self.image = image

		# If the user does not specify the dimensions use image.size
		self.dimensions = dimensions or image.size

	def json(self) -> dict[str, Any]:
		"""Get the object as a dict."""
		return {
			"name": self.name,
			"offsets": self.offsets,
			"opacity": self.opacity,
			"visible": self.visible,
			"dimensions": self.dimensions,
			"type": "LAYER",
			"blendmode": self.blendmode.name,
		}


class Group(LayerGroup):
	"""A representation of an image group."""

	def __init__(
		self,
		name: str,
		layers: list[Layer],
		dimensions: tuple[int, int] | None = None,
		offsets: tuple[int, int] = (0, 0),
		opacity: float = 1.0,
		visible: bool = True,
		blendmode: BlendType = BlendType.NORMAL,
	):
		"""Representation of an image group.

		Args:
			name (str): Name of the layer or group
			layers (layeredimage.Layer[]): A list of layers where the next
			index stacks upon the previous layer
			dimensions ((int, int)): A tuple representing the dimensions in
			pixels
			offsets (tuple, optional): A tuple representing the left and top
			offsets in pixels. Defaults to (0, 0).
			opacity (float, optional): A float representing the alpha value
			where 0 is invisible and 1 is fully visible. Defaults to 1.0.
			visible (bool, optional): Is the layer visible to the user (this
			is often configured per layer or per group by an 'eye' icon).
			Defaults to True.
			blendmode (Blendtype): The blending mode to use. Defaults to BlendType.NORMAL
		"""
		# Initialise dimens to 0 and then calculate as below
		super().__init__(
			name, (0, 0), offsets=offsets, opacity=opacity, visible=visible, blendmode=blendmode
		)
		self.layers = layers

		# If the user does not specify the dimensions use the largest x and y of
		# the layers
		self.dimensions = dimensions or (0, 0)
		if dimensions is None:
			layerDimens = [layer.dimensions for layer in layers]
			self.dimensions = (
				max(dimensions[0] for dimensions in layerDimens),
				max(dimensions[1] for dimensions in layerDimens),
			)

	def json(self) -> dict[str, Any]:
		"""Get the object as a dict."""
		layers = [layer.json() for layer in self.layers]
		return {
			"name": self.name,
			"offsets": self.offsets,
			"opacity": self.opacity,
			"visible": self.visible,
			"dimensions": self.dimensions,
			"type": "GROUP",
			"blendmode": self.blendmode.name,
			"layers": layers,
		}
