# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
from __future__ import (print_function, division, unicode_literals,
                        absolute_import)
import os
import sys
import pytest
from nipype import config
from mock import MagicMock
from builtins import object

try:
    import xvfbwrapper
    has_Xvfb = True
except ImportError:
    has_Xvfb = False

# Define mocks for xvfbwrapper. Do not forget the spec to ensure that
# hasattr() checks return False with missing attributes.
xvfbpatch = MagicMock(spec=['Xvfb'])
xvfbpatch.Xvfb.return_value = MagicMock(
    spec=['new_display', 'start', 'stop'], new_display=2010)

# Mock the legacy xvfbwrapper.Xvfb class (changed display attribute name)
xvfbpatch_old = MagicMock(spec=['Xvfb'])
xvfbpatch_old.Xvfb.return_value = MagicMock(
    spec=['vdisplay_num', 'start', 'stop'], vdisplay_num=2010)


@pytest.mark.parametrize('dispvar', [':12', 'localhost:12', 'localhost:12.1'])
def test_display_parse(monkeypatch, dispvar):
    """Check that when $DISPLAY is defined, the display is correctly parsed"""
    config._display = None
    config._config.remove_option('execution', 'display_variable')
    monkeypatch.setenv('DISPLAY', dispvar)
    assert config.get_display() == ':12'
    # Test that it was correctly cached
    assert config.get_display() == ':12'


@pytest.mark.parametrize('dispnum', range(5))
def test_display_config(monkeypatch, dispnum):
    """Check that the display_variable option is used ($DISPLAY not set)"""
    config._display = None
    dispstr = ':%d' % dispnum
    config.set('execution', 'display_variable', dispstr)
    monkeypatch.delitem(os.environ, 'DISPLAY', raising=False)
    assert config.get_display() == config.get('execution', 'display_variable')
    # Test that it was correctly cached
    assert config.get_display() == config.get('execution', 'display_variable')


@pytest.mark.parametrize('dispnum', range(5))
def test_display_system(monkeypatch, dispnum):
    """Check that when only a $DISPLAY is defined, it is used"""
    config._display = None
    config._config.remove_option('execution', 'display_variable')
    dispstr = ':%d' % dispnum
    monkeypatch.setenv('DISPLAY', dispstr)
    assert config.get_display() == dispstr
    # Test that it was correctly cached
    assert config.get_display() == dispstr


def test_display_config_and_system(monkeypatch):
    """Check that when only both config and $DISPLAY are defined, the config
    takes precedence"""
    config._display = None
    dispstr = ':10'
    config.set('execution', 'display_variable', dispstr)
    monkeypatch.setenv('DISPLAY', ':0')
    assert config.get_display() == dispstr
    # Test that it was correctly cached
    assert config.get_display() == dispstr


def test_display_noconfig_nosystem_patched(monkeypatch):
    """Check that when no $DISPLAY nor option are specified, a virtual Xvfb is
    used"""
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.delitem(os.environ, 'DISPLAY', raising=False)
    monkeypatch.setitem(sys.modules, 'xvfbwrapper', xvfbpatch)
    monkeypatch.setattr(sys, 'platform', value='linux')
    assert config.get_display() == ":2010"
    # Test that it was correctly cached
    assert config.get_display() == ':2010'

    # Check that raises in Mac
    config._display = None
    monkeypatch.setattr(sys, 'platform', value='darwin')
    with pytest.raises(RuntimeError):
        config.get_display()


def test_display_empty_patched(monkeypatch):
    """
    Check that when $DISPLAY is empty string and no option is specified,
    a virtual Xvfb is used
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.setenv('DISPLAY', '')
    monkeypatch.setitem(sys.modules, 'xvfbwrapper', xvfbpatch)
    monkeypatch.setattr(sys, 'platform', value='linux')
    assert config.get_display() == ':2010'
    # Test that it was correctly cached
    assert config.get_display() == ':2010'

    # Check that raises in Mac
    config._display = None
    monkeypatch.setattr(sys, 'platform', value='darwin')
    with pytest.raises(RuntimeError):
        config.get_display()

def test_display_noconfig_nosystem_patched_oldxvfbwrapper(monkeypatch):
    """
    Check that when no $DISPLAY nor option are specified,
    a virtual Xvfb is used (with a legacy version of xvfbwrapper).
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.delitem(os.environ, 'DISPLAY', raising=False)
    monkeypatch.setitem(sys.modules, 'xvfbwrapper', xvfbpatch_old)
    monkeypatch.setattr(sys, 'platform', value='linux')
    assert config.get_display() == ":2010"
    # Test that it was correctly cached
    assert config.get_display() == ':2010'

    # Check that raises in Mac
    config._display = None
    monkeypatch.setattr(sys, 'platform', value='darwin')
    with pytest.raises(RuntimeError):
        config.get_display()

def test_display_empty_patched_oldxvfbwrapper(monkeypatch):
    """
    Check that when $DISPLAY is empty string and no option is specified,
    a virtual Xvfb is used (with a legacy version of xvfbwrapper).
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.setenv('DISPLAY', '')
    monkeypatch.setitem(sys.modules, 'xvfbwrapper', xvfbpatch_old)
    monkeypatch.setattr(sys, 'platform', value='linux')
    assert config.get_display() == ':2010'
    # Test that it was correctly cached
    assert config.get_display() == ':2010'

    # Check that raises in Mac
    config._display = None
    monkeypatch.setattr(sys, 'platform', value='darwin')
    with pytest.raises(RuntimeError):
        config.get_display()

def test_display_noconfig_nosystem_notinstalled(monkeypatch):
    """
    Check that an exception is raised if xvfbwrapper is not installed
    but necessary (no config and $DISPLAY unset)
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.delenv('DISPLAY', raising=False)
    monkeypatch.setitem(sys.modules, 'xvfbwrapper', None)
    with pytest.raises(RuntimeError):
        config.get_display()


def test_display_empty_notinstalled(monkeypatch):
    """
    Check that an exception is raised if xvfbwrapper is not installed
    but necessary (no config and $DISPLAY empty)
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.setenv('DISPLAY', '')
    monkeypatch.setitem(sys.modules, 'xvfbwrapper', None)
    with pytest.raises(RuntimeError):
        config.get_display()


@pytest.mark.skipif(not has_Xvfb, reason='xvfbwrapper not installed')
@pytest.mark.skipif('darwin' in sys.platform, reason='macosx requires root for Xvfb')
def test_display_noconfig_nosystem_installed(monkeypatch):
    """
    Check that actually uses xvfbwrapper when installed (not mocked)
    and necessary (no config and $DISPLAY unset)
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.delenv('DISPLAY', raising=False)
    newdisp = config.get_display()
    assert int(newdisp.split(':')[-1]) > 1000
    # Test that it was correctly cached
    assert config.get_display() == newdisp


@pytest.mark.skipif(not has_Xvfb, reason='xvfbwrapper not installed')
@pytest.mark.skipif('darwin' in sys.platform, reason='macosx requires root for Xvfb')
def test_display_empty_installed(monkeypatch):
    """
    Check that actually uses xvfbwrapper when installed (not mocked)
    and necessary (no config and $DISPLAY empty)
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.setenv('DISPLAY', '')
    newdisp = config.get_display()
    assert int(newdisp.split(':')[-1]) > 1000
    # Test that it was correctly cached
    assert config.get_display() == newdisp


def test_display_empty_macosx(monkeypatch):
    """
    Check that an exception is raised if xvfbwrapper is necessary
    (no config and $DISPLAY unset) but platform is OSX. See
    https://github.com/nipy/nipype/issues/1400
    """
    config._display = None
    if config.has_option('execution', 'display_variable'):
        config._config.remove_option('execution', 'display_variable')
    monkeypatch.delenv('DISPLAY', '')

    monkeypatch.setattr(sys, 'platform', 'darwin')
    with pytest.raises(RuntimeError):
        config.get_display()


def test_cwd_cached(tmpdir):
    """Check that changing dirs does not change nipype's cwd"""
    oldcwd = config.cwd
    tmpdir.chdir()
    assert config.cwd == oldcwd


def test_debug_mode():
    from ... import logging

    sofc_config = config.get('execution', 'stop_on_first_crash')
    ruo_config = config.get('execution', 'remove_unnecessary_outputs')
    ki_config = config.get('execution', 'keep_inputs')
    wf_config = config.get('logging', 'workflow_level')
    if_config = config.get('logging', 'interface_level')
    ut_config = config.get('logging', 'utils_level')

    wf_level = logging.getLogger('nipype.workflow').level
    if_level = logging.getLogger('nipype.interface').level
    ut_level = logging.getLogger('nipype.utils').level

    config.enable_debug_mode()

    # Check config is updated and logging levels, too
    assert config.get('execution', 'stop_on_first_crash') == 'true'
    assert config.get('execution', 'remove_unnecessary_outputs') == 'false'
    assert config.get('execution', 'keep_inputs') == 'true'
    assert config.get('logging', 'workflow_level') == 'DEBUG'
    assert config.get('logging', 'interface_level') == 'DEBUG'
    assert config.get('logging', 'utils_level') == 'DEBUG'

    assert logging.getLogger('nipype.workflow').level == 10
    assert logging.getLogger('nipype.interface').level == 10
    assert logging.getLogger('nipype.utils').level == 10

    # Restore config and levels
    config.set('execution', 'stop_on_first_crash', sofc_config)
    config.set('execution', 'remove_unnecessary_outputs', ruo_config)
    config.set('execution', 'keep_inputs', ki_config)
    config.set('logging', 'workflow_level', wf_config)
    config.set('logging', 'interface_level', if_config)
    config.set('logging', 'utils_level', ut_config)
    logging.update_logging(config)

    assert config.get('execution', 'stop_on_first_crash') == sofc_config
    assert config.get('execution', 'remove_unnecessary_outputs') == ruo_config
    assert config.get('execution', 'keep_inputs') == ki_config
    assert config.get('logging', 'workflow_level') == wf_config
    assert config.get('logging', 'interface_level') == if_config
    assert config.get('logging', 'utils_level') == ut_config

    assert logging.getLogger('nipype.workflow').level == wf_level
    assert logging.getLogger('nipype.interface').level == if_level
    assert logging.getLogger('nipype.utils').level == ut_level
