SUFFIX_TO_BYTES = {
    "KI": 1024,
    "MI": 1024**2,
    "GI": 1024**3,
    "TI": 1024**4,
    "PI": 1024**5,
    "EI": 1024**6,
    "K": 1000,
    "M": 1000**2,
    "G": 1000**3,
    "T": 1000**4,
    "P": 1000**5,
    "E": 1000**6,
}


class ByteSize:
    """Convert multiples of bytes to various units."""

    def __init__(self, value):
        """
        Represents a quantity of bytes.

        `value` may be an integer, in which case it is assumed to be bytes.
        If value is a string, it is parsed as bytes if no suffix (Mi, M, Gi, G ...)
        is found.

        >>> values = [128974848, '129e6', '129M', '123Mi' ]
        >>> [ByteSize(v).to_unit('M') for v in values]
        ['128M', '129M', '129M', '128M']
        """
        self.value = parse_bytesize(value)

    def to_unit(self, unit=None, as_string=True):
        """unit must be `None` or one of Ki,Mi,Gi,Ti,Pi,Ei,K,M,G,T,P."""
        if unit is None:
            if as_string:
                return str(self.value)
            return self.value
        unit = unit.upper()
        new_value = int(self.value / SUFFIX_TO_BYTES[unit])
        if not as_string:
            return new_value
        return f"{new_value}{unit}"


def parse_bytesize(value):
    if isinstance(value, int) or isinstance(value, float):
        # Assume bytes
        return value
    value = value.upper()
    found_suffix = None
    for suffix in SUFFIX_TO_BYTES:
        if value.endswith(suffix):
            found_suffix = suffix
            break
    if found_suffix:
        value = value[: -len(found_suffix)]
    try:
        value = int(value)
    except ValueError:
        try:
            value = float(value)
        except ValueError:
            raise ValueError(f"{value} is not a valid integer or float value")
    if found_suffix:
        value = value * SUFFIX_TO_BYTES[found_suffix]
    return value
