# 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_morphology, NeuriteType

import pytest
from numpy.testing import assert_array_equal

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


def test_repeated_id():
    with pytest.raises(RawDataError):
        load_morphology(SWC_PATH / 'repeated_id.swc')


def test_neurite_followed_by_soma():
    with pytest.raises(MorphioError, match='Found a soma point with a neurite as parent'):
        load_morphology(SWC_PATH / 'soma_with_neurite_parent.swc')


def test_read_single_neurite():
    n = load_morphology(SWC_PATH / 'point_soma_single_neurite.swc')
    assert len(n.neurites) == 1
    assert n.neurites[0].root_node.id == 0
    assert_array_equal(n.soma.points,
                       [[0, 0, 0, 3.0]])
    assert len(n.neurites) == 1
    assert 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_morphology(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]])

    assert 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]])

    assert len(n.sections) == 2


def test_weird_indent():

    n = load_morphology("""

                 # 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_morphology(SWC_PATH / 'simple.swc')
    assert_array_equal(simple.points,
                       n.points)


def test_cyclic():
    with pytest.raises(RawDataError):
        load_morphology("""
        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_morphology(SWC_PATH / 'simple_reversed.swc')
    assert_array_equal(n.soma.points,
                       [[0, 0, 0, 1]])
    assert len(n.neurites) == 2
    assert 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():
    m = load_morphology(Path(SWC_PATH, 'custom_type.swc'))
    assert m.neurites[1].type == NeuriteType.custom5


def test_undefined_type():
    with pytest.raises(RawDataError, match='Unsupported section type: 0'):
        load_morphology(SWC_PATH / 'undefined_type.swc')
