"""Module defining tube containers."""

import enum
import warnings
from typing import Callable, Dict, List, Optional, Tuple, Union

from pylabrobot.resources.container import Container
from pylabrobot.resources.liquid import Liquid


class TubeBottomType(enum.Enum):
  """Enum for the type of bottom of a tube."""

  FLAT = "flat"
  U = "U"
  V = "V"
  UNKNOWN = "unknown"


class Tube(Container):
  """Tube container, like Eppendorf tubes.

  Note that in regular use these will be automatically generated by the
  :class:`pylabrobot.resources.TubeRack` class.
  """

  def __init__(
    self,
    name: str,
    size_x: float,
    size_y: float,
    size_z: float,
    max_volume: float,
    material_z_thickness: Optional[float] = None,
    category: str = "tube",
    model: Optional[str] = None,
    bottom_type: Union[TubeBottomType, str] = TubeBottomType.UNKNOWN,
    compute_volume_from_height: Optional[Callable[[float], float]] = None,
    compute_height_from_volume: Optional[Callable[[float], float]] = None,
    height_volume_data: Optional[Dict[float, float]] = None,
  ):
    """Create a new tube.

    Args:
      name: Name of the tube.
      size_x: Size of the tube in the x direction.
      size_y: Size of the tube in the y direction.
      size_z: Size of the tube in the z direction.
      material_z_thickness: Tube base to cavity base.
      max_volume: Maximum volume of the tube.
      category: Category of the tube.
      height_volume_data: Optional dict mapping height (mm) to volume (uL). See
        :class:`Container` for details.
    """
    if isinstance(bottom_type, str):
      bottom_type = TubeBottomType(bottom_type)

    super().__init__(
      name=name,
      size_x=size_x,
      size_y=size_y,
      size_z=size_z,
      material_z_thickness=material_z_thickness,
      category=category,
      max_volume=max_volume,
      model=model,
      compute_volume_from_height=compute_volume_from_height,
      compute_height_from_volume=compute_height_from_volume,
      height_volume_data=height_volume_data,
    )
    self.tracker.register_callback(self._state_updated)

  def serialize(self) -> dict:
    return {**super().serialize(), "max_volume": self.max_volume}

  def set_volume(self, volume: float):
    """Set the volume in the tube.

    (wraps :meth:`~.VolumeTracker.set_volume`)

    Example:
      Set the volume in a tube to 10 uL:

      >>> tube.set_volume(10)
    """

    self.tracker.set_volume(volume)

  def set_liquids(self, liquids: List[Tuple[Optional["Liquid"], float]]):
    """Set the liquids in the tube.

    Deprecated: use `set_volume` instead. This method will be removed in a future version.
    """

    warnings.warn(
      "`set_liquids` is deprecated and will be removed in a future version. Use `set_volume` instead.",
      DeprecationWarning,
      stacklevel=2,
    )

    self.tracker.set_liquids(liquids)
