
from typing import Optional
from yarnover.exceptions import PatternException

class Swatch:
    """Represents a square swatch, used for converting measurements to rows and stitches.
    """
    def __init__(self,
                 unit : str,
                 width : float,
                 height : float,
                 stitches : int,
                 rows : int,
                 **kwargs
                 ):
        """Convert units of measurement to stitches.

        :param unit: The name of the unit, i.e. "cm" og "in".
        :param width: The width of the swatch, measured in units.
        :param height: The height of the swatch, measured in units.
        :param stitches: The number of stitches in each row of the swatch.
        :param rows: The number of rows in the swatch.

        """
        self.unit = unit
        self.width = width
        self.height = height
        self.stitches = stitches
        self.rows = rows
        self.details = kwargs

    @property
    def stitches_per_unit(self):
        return float(self.stitches) / self.width

    @property
    def rows_per_unit(self):
        return float(self.rows) / self.height

    def units_to_stitches(self,
                          distance : float,
                          divisible_by : int = 1) -> int:
        """Convert units of measurement to stitches.

        :param distance: A distance, measured in the unit of the swatch.
        :param divisible_by: A number that the return value must be divisible by.
        """
        if divisible_by != 1:
            return divisible_by * self.units_to_stitches(distance / divisible_by)
        return round(distance * self.stitches_per_unit)

    def units_to_rows(self,
                      distance : float,
                      divisible_by : int = 1) -> int:
        """Convert units of measurement to rows.

        :param distance: A distance, measured in the unit of the swatch.
        :param divisible_by: A number that the return value must be divisible by.
        """
        if divisible_by != 1:
            return divisible_by * self.units_to_rows(distance / divisible_by)
        return round(distance * self.rows_per_unit)

    def stitches_to_units(self, stitches : int) -> float:
        """Convert stitches to units of measurement.

        :param stitches: The number of stitches
        """
        return round(stitches / self.stitches_per_unit)

    def rows_to_units(self, rows : int) -> float:
        """Convert rows to units of measurement.

        :param rows: The number of rows to convert.
        """
        return round(rows / self.rows_per_unit)

    def __str__(self):
        return f"{self.stitches} stitches and {self.rows} rows per {self.width}x{self.height} {self.unit}"

class MetricSwatch(Swatch):
    """Represents a 10 cm by 10 cm swatch.
    """
    def __init__(self,
                 stitches_per_10_cm : int,
                 rows_per_10_cm : int,
                 **kwargs):
        super().__init__(
            "cm",
            10,
            10,
            stitches_per_10_cm,
            rows_per_10_cm,
            **kwargs
        )

    def cm_to_stitches(self, cm : float, divisible_by : int = 1) -> int:
        """Convert measurements in cm to stitches
        
        :param cm: The number of cm to convert
        :param divisible_by: A number that the return value must be divisible by.
        :return: A number of stitches
        """
        return self.units_to_stitches(cm, divisible_by)

    def cm_to_rows(self, cm : float, divisible_by : int = 1) -> int:
        """Convert measurements in cm to rows

        :param cm: The number of cm to convert
        :param divisible_by: A number that the return value must be divisible by.
        :return: A number of rows
        """
        return self.units_to_rows(cm, divisible_by)

    def stitches_to_cm(self, stitches : int) -> float:
        return self.stitches_to_units(stitches)

    def row_to_cm(self, rows : int) -> float:
        return self.rows_to_units(rows)

if __name__=="__main__":
    print(MetricSwatch(13, 30, ""))