import pytest
import os
import logging
import numpy as np
import tempfile

from ophyd.utils import epics_pvs as epics_utils
from ophyd.utils import (make_dir_tree, makedirs, set_and_wait)
from ophyd import Signal


logger = logging.getLogger(__name__)


def test_split():
    utils = epics_utils

    assert utils.split_record_field('record.field') == ('record', 'field')
    assert utils.split_record_field('record.field.invalid') == ('record.field',
                                                                'invalid')
    assert utils.strip_field('record.field') == 'record'
    assert utils.strip_field('record.field.invalid') == 'record.field'
    assert utils.record_field('record', 'field') == 'record.FIELD'


def test_waveform_to_string():
    s = 'abcdefg'
    asc = [ord(c) for c in s]
    assert epics_utils.waveform_to_string(asc) == s

    asc = [ord(c) for c in s] + [0, 0, 0]
    assert epics_utils.waveform_to_string(asc) == s


def test_records_from_db():
    # db_dir = os.path.join(config.epics_base, 'db')

    # if os.path.exists(db_dir):
    #     # fall back on the db file included with the tests
    db_dir = os.path.dirname(__file__)
    db_path = os.path.join(db_dir, 'scaler.db')
    records = epics_utils.records_from_db(db_path)
    assert ('bo', '$(P)$(S)_calcEnable') in records


@pytest.mark.parametrize('value, dtype, shape', [
    [1, 'integer', []],
    [1.0, 'number', []],
    [1e-3, 'number', []],
    ['foo', 'string', []],
    [np.array([1, 2, 3]), 'array', [3]],
    [np.array([[1, 2], [3, 4]]), 'array', [2, 2]],
    [(1, 2, 3), 'array', [3]],
    [[1, 2, 3], 'array', [3]],
    [[], 'array', [0]]
]
)
def test_data_type_and_shape(value, dtype, shape):
    utils = epics_utils
    assert utils.data_type(value) == dtype
    assert utils.data_shape(value) == shape


@pytest.mark.parametrize('value',
                         [dict()])
def test_invalid_data_type(value):
    utils = epics_utils
    with pytest.raises(ValueError):
        utils.data_type(value)


def assert_OD_equal_ignore_ts(a, b):
    for (k1, v1), (k2, v2) in zip(a.items(), b.items()):
        assert (k1 == k2) and (v1['value'] == v2['value'])


def assert_file_mode(path, expected):
    assert (os.stat(path).st_mode & 0o777) == expected


def test_makedirs():
    with tempfile.TemporaryDirectory() as tempdir:
        create_dir = os.path.join(tempdir, 'a')
        makedirs(create_dir, mode=0o767, mode_base=tempdir)
        assert_file_mode(create_dir, 0o767)


def test_make_dir_tree():
    with tempfile.TemporaryDirectory() as tempdir:
        paths = make_dir_tree(2016, base_path=tempdir, mode=0o777)
        assert len(paths) == 366

        for path in paths:
            assert_file_mode(path, 0o777)

        assert os.path.join(tempdir, '2016', '03', '04') in paths
        assert os.path.join(tempdir, '2016', '02', '29') in paths


def test_valid_pvname():
    with pytest.raises(epics_utils.BadPVName):
        epics_utils.validate_pv_name('this.will.fail')


def test_array_into_softsignal():
    data = np.array([1, 2, 3])
    s = Signal(name='np.array')
    set_and_wait(s, data)
    assert np.all(s.get() == data)
