from onetick.py.core.column_operations.base import _Operation
class _MaxOperator(_Operation):
    def __init__(self, objs):
        from onetick.py.types import get_type_by_objects
        super().__init__(dtype=get_type_by_objects(objs))
        def _str_max(l_val, r_val):
            if type(r_val) is list:
                if len(r_val) > 1:
                    r_val = _str_max(r_val[0], r_val[1:])
                else:
                    r_val = r_val[0]
            return f'CASE({l_val} > {r_val}, 1, {l_val}, {r_val})'
        self._repr = _str_max(objs[0], objs[1:])
    def __str__(self):
        return self._repr
[docs]def max(*objs):
    """
    Returns maximum value from list of ``objs``.
    Parameters
    ----------
    objs: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['MAX'] = otp.math.max(5, data['A'])
    >>> otp.run(data)
            Time  A  MAX
    0 2003-12-01  1    5
    """
    return _MaxOperator(list(objs)) 
class _MinOperator(_Operation):
    def __init__(self, objs):
        from onetick.py.types import get_type_by_objects
        super().__init__(dtype=get_type_by_objects(objs))
        def _str_min(l_val, r_val):
            if type(r_val) is list:
                if len(r_val) > 1:
                    r_val = _str_min(r_val[0], r_val[1:])
                else:
                    r_val = r_val[0]
            return f'CASE({l_val} < {r_val}, 1, {l_val}, {r_val})'
        self._repr = _str_min(objs[0], objs[1:])
    def __str__(self):
        return self._repr
[docs]def min(*objs):
    """
    Returns minimum value from list of ``objs``.
    Parameters
    ----------
    objs: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['MIN'] = otp.math.min(-5, data['A'])
    >>> otp.run(data)
            Time  A  MIN
    0 2003-12-01  1   -5
    """
    return _MinOperator(list(objs)) 
class _RandomFunc(_Operation):
    """
    It implements the `rand` built-in function.
    """
    def __init__(self, min_value: int, max_value: int, seed: int = None):
        super().__init__(dtype=int)
        def _repr(min_value, max_value, seed):
            result = f'rand({min_value}, {max_value}'
            if seed is not None:
                result += f',{seed})'
            else:
                result += ')'
            return result
        self._repr = _repr(min_value, max_value, seed)
    def __str__(self):
        return self._repr
[docs]def rand(min_value: int, max_value: int, seed: int = None):
    """
    Returns a pseudo-random value in the range between ``min_value`` and ``max_value``.
    If ``seed`` is not specified, the function produces different values each time a query is invoked.
    If ``seed`` is specified, for this seed the function produces the same sequence of values
    each time a query is invoked.
    Parameters
    ----------
    min_value: int, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    max_value: int, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    seed: int, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['RAND'] = otp.math.rand(1, 1000)
    """
    if isinstance(min_value, int) and min_value < 0:
        raise Exception("It is not possible to use negative values for the `min_value`")
    if isinstance(min_value, int) and isinstance(max_value, int) and min_value >= max_value:
        raise Exception("The `max_value` parameter should be more than `min_value`")
    return _RandomFunc(min_value, max_value, seed) 
class _Now(_Operation):
    def __init__(self):
        from onetick.py.types import nsectime
        super().__init__(dtype=nsectime)
        def _repr():
            return 'now()'
        self._repr = _repr()
    def __str__(self):
        return self._repr
# TODO: this is not math, let's move it somewhere else
[docs]def now():
    """
    Returns the current time expressed as the number of milliseconds since the UNIX epoch.
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['NOW'] = otp.now()
    """
    return _Now() 
class _Ln(_Operation):
    """
    Compute the natural logarithm.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'LOG({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def ln(value):
    """
    Compute the natural logarithm of the ``value``.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['LN'] = otp.math.ln(2.718282)
    >>> otp.run(data)
            Time  A   LN
    0 2003-12-01  1  1.0
    See Also
    --------
    onetick.py.math.exp
    """
    return _Ln(value) 
class _Log10(_Operation):
    """
    Compute the base-10 logarithm.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'LOG10({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def log10(value):
    """
    Compute the base-10 logarithm of the ``value``.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['LOG10'] = otp.math.log10(100)
    >>> otp.run(data)
            Time  A  LOG10
    0 2003-12-01  1    2.0
    """
    return _Log10(value) 
class _Exp(_Operation):
    """
    Compute the natural exponent.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'EXP({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def exp(value):
    """
    Compute the natural exponent of the ``value``.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['E'] = otp.math.exp(1)
    >>> otp.run(data)
            Time  A         E
    0 2003-12-01  1  2.718282
    See Also
    --------
    onetick.py.math.ln
    """
    return _Exp(value) 
class _Sqrt(_Operation):
    """
    Compute the square root.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'SQRT({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def sqrt(value):
    """
    Compute the square root of the ``value``.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['SQRT'] = otp.math.sqrt(4)
    >>> otp.run(data)
            Time  A  SQRT
    0 2003-12-01  1   2.0
    """
    return _Sqrt(value) 
class _Sign(_Operation):
    """
    Get the sign of value.
    """
    def __init__(self, value):
        super().__init__(dtype=int)
        def _repr(value):
            return f'SIGN({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def sign(value):
    """
    Compute the sign of the ``value``.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['SIGN_POS'] = otp.math.sign(123)
    >>> data['SIGN_ZERO'] = otp.math.sign(0)
    >>> data['SIGN_NEG'] = otp.math.sign(-123)
    >>> otp.run(data)
            Time  A  SIGN_POS  SIGN_ZERO  SIGN_NEG
    0 2003-12-01  1         1          0        -1
    """
    return _Sign(value) 
class _Power(_Operation):
    """
    Compute the ``base`` to the power of the ``exponent``.
    """
    def __init__(self, base, exponent):
        super().__init__(dtype=float)
        def _repr(base, exponent):
            return f'POWER({base}, {exponent})'
        self._repr = _repr(base, exponent)
    def __str__(self):
        return self._repr
[docs]def pow(base, exponent):
    """
    Compute the ``base`` to the power of the ``exponent``.
    Parameters
    ----------
    base: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    exponent: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=2)
    >>> data['RES'] = otp.math.pow(data['A'], 10)
    >>> otp.run(data)
            Time  A     RES
    0 2003-12-01  2  1024.0
    """
    return _Power(base, exponent) 
class _Pi(_Operation):
    def __init__(self):
        super().__init__(dtype=float)
        def _repr():
            return 'PI()'
        self._repr = _repr()
    def __str__(self):
        return self._repr
[docs]def pi():
    """
    Returns the value of Pi number.
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['PI'] = otp.math.pi()
    >>> otp.run(data)
            Time  A        PI
    0 2003-12-01  1  3.141593
    """
    return _Pi() 
class _Sin(_Operation):
    """
    Returns the value of trigonometric function `sin` for the given angle number expressed in radians.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'SIN({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def sin(value):
    """
    Returns the value of trigonometric function `sin` for the given ``value`` number expressed in radians.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['SIN'] = otp.math.sin(otp.math.pi() / 6)
    >>> otp.run(data)
            Time  A  SIN
    0 2003-12-01  1  0.5
    See Also
    --------
    onetick.py.math.pi
    """
    return _Sin(value) 
class _Cos(_Operation):
    """
    Returns the value of trigonometric function `cos` for the given angle number expressed in radians.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'COS({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def cos(value):
    """
    Returns the value of trigonometric function `cos` for the given ``value`` number expressed in radians.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['COS'] = otp.math.cos(otp.math.pi() / 3)
    >>> otp.run(data)
            Time  A  COS
    0 2003-12-01  1  0.5
    See Also
    --------
    onetick.py.math.pi
    """
    return _Cos(value) 
class _Tan(_Operation):
    """
    Returns the value of trigonometric function `tan` for the given angle number expressed in radians.
    """
    def __init__(self, value):
        super().__init__(dtype=float)
        def _repr(value):
            return f'TAN({value})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def tan(value):
    """
    Returns the value of trigonometric function `tan` for the given ``value`` number expressed in radians.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1)
    >>> data['TAN'] = otp.math.tan(otp.math.pi() / 4)
    >>> otp.run(data)
            Time  A  TAN
    0 2003-12-01  1  1.0
    See Also
    --------
    onetick.py.math.pi
    """
    return _Tan(value) 
class _Mod(_Operation):
    """
    Implements the remainder from dividing ``value1`` by ``value2``
    """
    def __init__(self, value1, value2):
        super().__init__(dtype=int)
        def _repr(value1, value2):
            return f'MOD({value1}, {value2})'
        self._repr = _repr(value1, value2)
    def __str__(self):
        return self._repr
[docs]def mod(value1, value2):
    """
    Computes the remainder from dividing ``value1`` by ``value2``
    Parameters
    ----------
    value1: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    value2: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=100)
    >>> data['MOD'] = otp.math.mod(data['A'], 72)
    >>> otp.run(data)
            Time    A  MOD
    0 2003-12-01  100   28
    """
    return _Mod(value1, value2) 
class _Floor(_Operation):
    """
    Returns a long integer value representing the largest integer that is less than or equal to the `value`.
    """
    def __init__(self, value):
        super().__init__(dtype=int)
        def _repr(val):
            return f'FLOOR({val})'
        self._repr = _repr(value)
    def __str__(self):
        return self._repr
[docs]def floor(value):
    """
    Returns a long integer value representing the largest integer that is less than or equal to the `value`.
    Parameters
    ----------
    value: int, float, :py:class:`~onetick.py.Operation`, :py:class:`~onetick.py.Column`
    Return
    ------
        :py:class:`~onetick.py.Operation`
    Examples
    --------
    >>> data = otp.Tick(A=1.2)
    >>> data['FLOOR'] = otp.math.floor(data['A'])
    >>> otp.run(data)
            Time    A  FLOOR
    0 2003-12-01  1.2      1
    """
    return _Floor(value)