
from . import duration
from . import memory


class String:
    r'''
Rich string.

.. note::

    All options are attributes, that can be modified at all times.

:options:

    **data** (``<str>`` | ``None``)
        The data.

    **width** ([``None``] | ``<int>``)
        Print width (formatted print only).

    **color** ([``None``] | ``<str>``)
        Print color, e.g. "1;32" for bold green (formatted print only).

    **align** ([``'<'``] | ``'>'``)
        Print alignment (formatted print only).

    **dummy** ([``0``] | ``<int>`` | ``<float>``)
        Dummy numerical value.

:methods:

    **A.format()**
        Formatted string.

    **str(A)**
        Unformatted string.

    **A.isnumeric()**
        Return if the "data" is numeric.

    **int(A)**
        Dummy integer.

    **float(A)**
        Dummy float.
    '''


    def __init__(self, data, width=None, align='<', color=None, dummy=0):

        self.data = data
        self.width = width
        self.color = color
        self.align = align
        self.dummy = dummy


    def format(self):
        r'''
Return formatted string: align/width/color are applied.
        '''

        if self.width and self.color:
            fmt = '\x1b[{color:s}m{{0:{align:s}{width:d}.{width:d}s}}\x1b[0m'.format(
                **self.__dict__)
        elif self.width:
            fmt = '{{0:{align:s}{width:d}.{width:d}s}}'.format(**self.__dict__)
        elif self.color:
            fmt = '\x1b[{color:s}m{{0:{align:s}s}}\x1b[0m'.format(**self.__dict__)
        else:
            fmt = '{{0:{align:s}s}}'.format(**self.__dict__)

        return fmt.format(str(self))


    def isnumeric(self):
        r'''
Return if the "data" is numeric : always zero for this class.
        '''

        return False


    def __str__(self):

        return str(self.data)


    def __int__(self):

        return int(self.dummy)


    def __float__(self):

        return float(self.dummy)


    def __repr__(self):

        return str(self)


    def __lt__(self, other):

        return str(self) < str(other)


class Integer(String):
    r'''
Rich integer.

.. note::

    All options are attributes, that can be modified at all times.

:options:

    **data** (``<str>`` | ``None``)
        The data.

    **width** ([``None``] | ``<int>``)
        Print width (formatted print only).

    **color** ([``None``] | ``<str>``)
        Print color, e.g. "1;32" for bold green (formatted print only).

    **align** ([``'<'``] | ``'>'``)
        Print alignment (formatted print only).

    **dummy** ([``0``] | ``<int>`` | ``<float>``)
        Dummy numerical value, used in case of non-numerical ``data``.

:methods:

    **A.format()**
        Formatted string.

    **str(A)**
        Unformatted string.

    **A.isnumeric()**
        Return if the "data" is numeric.

    **int(A)**
        Return ``data`` as integer (``dummy`` is returned if ``data`` is not numeric).

    **float(A)**
        Return ``data`` as float (``dummy`` is returned if ``data`` is not numeric).
    '''


    def __init__(self, data, **kwargs):

        try:
            data = int(data)
        except BaseException:
            pass

        super().__init__(data, **kwargs)


    def isnumeric(self):
        r'''
Return if the "data" is numeric : always zero for this class.
        '''

        if isinstance(self.data, int):
            return True
        if isinstance(self.data, float):
            return True

        return False


    def __int__(self):

        if isinstance(self.data, int):
            return self.data
        if isinstance(self.data, float):
            return int(self.data)

        return int(self.dummy)


    def __float__(self):

        if isinstance(self.data, int):
            return float(self.data)
        if isinstance(self.data, float):
            return self.data

        return float(self.dummy)


    def __lt__(self, other):

        if self .isnumeric() and other.isnumeric():
            return int(self) < int(other)
        elif self .isnumeric():
            return 0
        elif other.isnumeric():
            return -1

        return self.data < other.data


class Float(String):
    r'''
Rich float.

.. note::

    All options are attributes, that can be modified at all times.

:options:

    **data** (``<str>`` | ``None``)
        The data.

    **width** ([``None``] | ``<int>``)
        Print width (formatted print only).

    **color** ([``None``] | ``<str>``)
        Print color, e.g. "1;32" for bold green (formatted print only).

    **align** ([``'<'``] | ``'>'``)
        Print alignment (formatted print only).

    **precision** ([``2``] | ``<int>``)
        Print precision (formatted print only).

    **dummy** ([``0``] | ``<int>`` | ``<float>``)
        Dummy numerical value, used in case of non-numerical ``data``.

:methods:

    **A.format()**
        Formatted string.

    **str(A)**
        Unformatted string.

    **A.isnumeric()**
        Return if the "data" is numeric.

    **int(A)**
        Return ``data`` as integer (``dummy`` is returned if ``data`` is not numeric).

    **float(A)**
        Return ``data`` as float (``dummy`` is returned if ``data`` is not numeric).
    '''


    def __init__(self, data, **kwargs):

        try:
            data = float(data)
        except BaseException:
            pass

        self.precision = kwargs.pop('precision', 2)

        super().__init__(data, **kwargs)


    def isnumeric(self):
        r'''
Return if the "data" is numeric : always zero for this class.
        '''

        if isinstance(self.data, float):
            return True
        if isinstance(self.data, int):
            return True

        return False


    def __str__(self):

        if not self.isnumeric():
            return self.data

        return '{{0:.{precision:d}f}}'.format(**self.__dict__).format(self.data)


    def __int__(self):

        if isinstance(self.data, float):
            return int(self.data)
        if isinstance(self.data, int):
            return self.data

        return int(self.dummy)


    def __float__(self):

        if isinstance(self.data, float):
            return self.data
        if isinstance(self.data, int):
            return float(self.data)

        return float(self.dummy)


    def __lt__(self, other):

        if self .isnumeric() and other.isnumeric():
            return float(self) < float(other)
        elif self .isnumeric():
            return 0
        elif other.isnumeric():
            return -1

        return self.data < other.data


class Duration(Integer):
    r'''
Rich duration (seconds).

.. note::

    All options are attributes, that can be modified at all times.

:options:

    **data** (``<str>`` | ``None``)
        The data.

    **width** ([``None``] | ``<int>``)
        Print width (formatted print only).

    **color** ([``None``] | ``<str>``)
        Print color, e.g. "1;32" for bold green (formatted print only).

    **align** ([``'<'``] | ``'>'``)
        Print alignment (formatted print only).

    **precision** ([``None``] | ``<int>``)
        Print precision to use for the conversion (formatted print only). ``None`` means automatic
        precision. See ``GooseSLURM.duration.asHuman``.

    **dummy** ([``0``] | ``<int>`` | ``<float>``)
        Dummy numerical value, used in case of non-numerical ``data``.

:methods:

    **A.format()**
        Formatted string, after unit conversion.

    **str(A)**
        Unformatted string, after unit conversion.

    **A.isnumeric()**
        Return if the "data" is numeric.

    **int(A)**
        Return ``data`` as integer (``dummy`` is returned if ``data`` is not numeric).

    **float(A)**
        Return ``data`` as float (``dummy`` is returned if ``data`` is not numeric).
    '''


    def __init__(self, data, **kwargs):

        data = duration.asSeconds(data, default=data)

        self.precision = kwargs.pop('precision', None)

        super().__init__(data, **kwargs)


    def __str__(self):

        if not self.isnumeric():
            return self.data

        return duration.asHuman(self.data, self.precision)


class Memory(Integer):
    r'''
Rich memory (bytes).

.. note::

    All options are attributes, that can be modified at all times.

:options:

    **data** (``<str>`` | ``None``)
        The data.

    **width** ([``None``] | ``<int>``)
        Print width (formatted print only).

    **color** ([``None``] | ``<str>``)
        Print color, e.g. "1;32" for bold green (formatted print only).

    **align** ([``'<'``] | ``'>'``)
        Print alignment (formatted print only).

    **precision** ([``None``] | ``<int>``)
        Print precision to use for the conversion (formatted print only). ``None`` means automatic
        precision. See ``GooseSLURM.memory.asHuman``.

    **dummy** ([``0``] | ``<int>`` | ``<float>``)
        Dummy numerical value, used in case of non-numerical ``data``.

    **default_unit** (``int``)
        The unit to assume if no unit if specified (specify the number of bytes).

:methods:

    **A.format()**
        Formatted string, after unit conversion.

    **str(A)**
        Unformatted string, after unit conversion.

    **A.isnumeric()**
        Return if the "data" is numeric.

    **int(A)**
        Return ``data`` as integer (``dummy`` is returned if ``data`` is not numeric).

    **float(A)**
        Return ``data`` as float (``dummy`` is returned if ``data`` is not numeric).
    '''


    def __init__(self, data, **kwargs):

        if 'default_unit' in kwargs:
            data = memory.asBytes(data, default=data, default_unit=kwargs.pop('default_unit'))
        else:
            data = memory.asBytes(data, default=data)

        self.precision = kwargs.pop('precision', None)

        super().__init__(data, **kwargs)


    def __str__(self):

        if not self.isnumeric():
            return self.data

        return memory.asHuman(self.data, self.precision)
