# Copyright (c) 2015, Ecole Polytechnique Federale de Lausanne, Blue Brain Project
# All rights reserved.
#
# This file is part of NeuroM <https://github.com/BlueBrain/NeuroM>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     1. Redistributions of source code must retain the above copyright
#        notice, this list of conditions and the following disclaimer.
#     2. Redistributions in binary form must reproduce the above copyright
#        notice, this list of conditions and the following disclaimer in the
#        documentation and/or other materials provided with the distribution.
#     3. Neither the name of the copyright holder nor the names of
#        its contributors may be used to endorse or promote products
#        derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from pathlib import Path

import numpy as np

from morphio import RawDataError, MorphioError
from neurom import load_neuron, NeuriteType
from numpy.testing import assert_array_equal, assert_equal

from nose import tools as nt


DATA_PATH = Path(__file__).parent.parent.parent.parent / 'test_data'
SWC_PATH = Path(DATA_PATH, 'swc')
SWC_SOMA_PATH = Path(SWC_PATH, 'soma')


@nt.raises(RawDataError)
def test_repeated_id():
    load_neuron(SWC_PATH / 'repeated_id.swc')


def test_neurite_followed_by_soma():
    nt.assert_raises(MorphioError, load_neuron, SWC_PATH / 'soma_with_neurite_parent.swc')
    try:
        load_neuron(SWC_PATH / 'soma_with_neurite_parent.swc')
    except MorphioError as e:
        nt.assert_in('Warning: found a disconnected neurite', e.args[0])


def test_read_single_neurite():
    n = load_neuron(SWC_PATH / 'point_soma_single_neurite.swc')
    nt.eq_(len(n.neurites), 1)
    nt.eq_(n.neurites[0].root_node.id, 0)
    assert_array_equal(n.soma.points,
                       [[0, 0, 0, 3.0]])
    nt.eq_(len(n.neurites), 1)
    nt.eq_(len(n.sections), 1)
    assert_array_equal(n.neurites[0].points,
                       np.array([[0, 0, 2, 0.5],
                                 [0, 0, 3, 0.5],
                                 [0, 0, 4, 0.5],
                                 [0, 0, 5, 0.5]]))


def test_read_split_soma():
    n = load_neuron(SWC_PATH / 'split_soma_two_neurites.swc')

    assert_array_equal(n.soma.points,
                       [[1, 0, 1, 4.0],
                        [2, 0, 0, 4.0],
                        [3, 0, 0, 4.0]])

    nt.assert_equal(len(n.neurites), 2)
    assert_array_equal(n.neurites[0].points,
                       [[0, 0, 2, 0.5],
                        [0, 0, 3, 0.5],
                        [0, 0, 4, 0.5],
                        [0, 0, 5, 0.5]])

    assert_array_equal(n.neurites[1].points,
                       [[0, 0, 6, 0.5],
                        [0, 0, 7, 0.5],
                        [0, 0, 8, 0.5],
                        [0, 0, 9, 0.5]])

    nt.eq_(len(n.sections), 2)


def test_weird_indent():

    n = load_neuron("""

                 # this is the same as simple.swc

# but with a questionable styling

     1 1  0  0 0 1. -1
 2 3  0  0 0 1.  1

 3 3  0  5 0 1.  2
 4 3 -5  5 0 0.  3



 5 3  6  5 0 0.  3
     6 2  0  0 0 1.  1
 7 2  0 -4 0 1.  6

 8 2  6 -4 0         0.  7
 9 2 -5 -4 0 0.  7
""", reader='swc')

    simple = load_neuron(SWC_PATH / 'simple.swc')
    assert_array_equal(simple.points,
                       n.points)


@nt.raises(RawDataError)
def test_cyclic():
    load_neuron("""
    1 1  0  0 0 1. -1
    2 3  0  0 0 1.  1
    3 3  0  5 0 1.  2
    4 3 -5  5 0 0.  3
    5 3  6  5 0 0.  3
    6 2  0  0 0 1.  6  # <-- cyclic point
    7 2  0 -4 0 1.  6
    8 2  6 -4 0 0.  7
    9 2 -5 -4 0 0.  7""", reader='swc')


def test_simple_reversed():
    n = load_neuron(SWC_PATH / 'simple_reversed.swc')
    assert_array_equal(n.soma.points,
                       [[0, 0, 0, 1]])
    nt.assert_equal(len(n.neurites), 2)
    nt.assert_equal(len(n.neurites[0].points), 4)
    assert_array_equal(n.neurites[0].points,
                       [[0, 0, 0, 1],
                        [0, 5, 0, 1],
                        [-5, 5, 0, 0],
                        [6, 5, 0, 0]])
    assert_array_equal(n.neurites[1].points,
                       [[0, 0, 0, 1],
                        [0, -4, 0, 1],
                        [6, -4, 0, 0],
                        [-5, -4, 0, 0]])


def test_custom_type():
    neuron = load_neuron(Path(SWC_PATH, 'custom_type.swc'))
    assert_equal(neuron.neurites[1].type, NeuriteType.custom)


def test_undefined_type():
    nt.assert_raises(RawDataError, load_neuron, SWC_PATH / 'undefined_type.swc')
    try:
        load_neuron(SWC_PATH / 'undefined_type.swc')
    except RawDataError as e:
        nt.assert_in('Unsupported section type: 0', e.args[0])
