"""
.. module:: test_common
   :synopsis: Unit tests for common module
"""

from __future__ import print_function

import sys
import time

from pytest import approx
from time import sleep
from nutsflow import MeanStd
from nutsflow.common import (sec_to_hms, timestr, Redirect, as_tuple, as_set,
                             as_list, is_iterable, colfunc, console,
                             itemize, StableRandom, Timer)


def test_is_iterable():
    assert is_iterable([1, 2])
    assert not is_iterable('12')


def test_as_tuple():
    assert as_tuple(1) == (1,)
    assert as_tuple((1, 2)) == (1, 2)
    assert as_tuple([1, 2]) == (1, 2)


def test_as_list():
    assert as_list(1) == [1]
    assert as_list((1, 2)) == [1, 2]
    assert as_list([1, 2]) == [1, 2]


def test_as_set():
    assert as_set(1) == (1,)
    assert as_set((1, 2)) == {1, 2}
    assert as_set([1, 2]) == {1, 2}


def test_itemize():
    assert itemize([]) == []
    assert itemize([3]) == 3
    assert itemize([3, 2, 1]) == [3, 2, 1]


def test_sec_to_hms():
    assert sec_to_hms('80') == (0, 1, 20)
    assert sec_to_hms(3 * 60 * 60 + 2 * 60 + 1) == (3, 2, 1)


def test_timestr():
    assert timestr('') == ''
    assert timestr('80') == '0:01:20'


def test_colfunc():
    data = ['a3', 'b2', 'c1']
    assert list(map(colfunc(None), data)) == data
    assert list(map(colfunc(0), data)) == ['a', 'b', 'c']
    assert list(map(colfunc(1), data)) == ['3', '2', '1']
    expected = [['3', 'a'], ['2', 'b'], ['1', 'c']]
    assert list(map(colfunc((1, 0)), data)) == expected


def test_console():
    with Redirect() as out:
        console('test')
    assert out.getvalue() == 'test\n'


def test_Redirect():
    with Redirect() as out:
        print('test')
    assert out.getvalue() == 'test\n'

    with Redirect('STDERR') as out:
        print('error', file=sys.stderr)
    assert out.getvalue() == 'error\n'


def test_StableRandom():
    rnd = StableRandom(1)
    assert rnd.randint(1, 10) == 5
    assert rnd.uniform(-10, 10) == approx(9.943696167306904)
    assert rnd.random() == approx(0.7203244894557457)
    assert rnd.sample(range(10), 3) == [9, 0, 1]

    lst = [1, 2, 3, 4, 5]
    rnd.shuffle(lst)
    assert lst == [5, 3, 1, 4, 2]

    rnd = StableRandom()
    assert max(rnd.random() for _ in range(1000)) < 1.0
    assert min(rnd.random() for _ in range(1000)) >= 0.0

    rnd1, rnd2 = StableRandom(0), StableRandom(0)
    for _ in range(100):
        assert rnd1.random() == rnd2.random()

    rnd1, rnd2 = StableRandom(0), StableRandom(0)
    rnd2.jumpahead(10)
    for _ in range(100):
        assert rnd1.random() != rnd2.random()
    rnd2.setstate(rnd1.getstate())
    for _ in range(100):
        assert rnd1.random() == rnd2.random()

    rnd1, rnd2 = StableRandom(0), StableRandom(1)
    for _ in range(100):
        assert rnd1.random() != rnd2.random()

    rnd1 = StableRandom()
    sleep(0.5)  # seed is based on system time.
    rnd2 = StableRandom()
    for _ in range(100):
        assert rnd1.random() != rnd2.random()

    rnd = StableRandom()
    numbers = [rnd._randbelow(10) for _ in range(1000)]
    assert max(numbers) < 10
    assert min(numbers) >= 0

    rnd = StableRandom()
    numbers = [rnd.gauss_next() for _ in range(10000)]
    my, std = numbers >> MeanStd()
    assert 0.0 == approx(my, abs=0.1)
    assert 1.0 == approx(std, abs=0.1)


def test_timer():
    t = Timer()
    time.sleep(1.3)
    assert str(t) == '00:01'

    with Timer() as t:
        time.sleep(1.3)
    assert str(t) == '00:01'

    t = Timer()
    time.sleep(0.5)
    t.start()
    time.sleep(1.3)
    t.stop()
    time.sleep(0.5)
    assert str(t) == '00:01'
