
from typing import Optional
from yarnover.exceptions import KnittingException


Distance = float

class Swatch:
    """Represents a swatch, used for converting measurements to rows and stitches.
    """
    def __init__(self,
                 swatch_unit_count : int,
                 unit : str,
                 swatch_stitches,
                 swatch_rows):
        self.swatch_unit_count = swatch_unit_count
        self.unit = unit
        self.stitches_per_unit = swatch_stitches / swatch_unit_count
        self.rows_per_unit = swatch_rows / swatch_unit_count

    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
        """
        if divisible_by != 1:
            return divisible_by * self.units_to_stitches(distance / divisible_by)
        return round(distance * self.stitches_per_unit)

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

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

    def units_to_rows(self,
                distance : float,
                divisible_by : int = 1) -> int:
        """Convert units of measurement to rows"""
        if divisible_by != 1:
            return divisible_by * self.units_to_rows(distance / divisible_by)
        return round(distance * self.rows_per_unit)

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

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

class MetricSwatch(Swatch):
    def __init__(self, stitches_per_10_cm : int, rows_per_10_cm : Optional[int] = None):
        """Initiate a metric swatch from row and stitch counts for a 10 cm swatch.
        """
        super().__init__(
            10,
            "cm",
            stitches_per_10_cm,
            rows_per_10_cm
        )

    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)


if __name__=="__main__":
    print(MetricSwatch(29, 31).cm_to_stitches(10, 4))
    print(MetricSwatch(29, 31).cm_to_rows(10, 4))
