package CH.ifa.draw.standard;

import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;
import java.util.Vector;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

import CH.ifa.draw.figures.PolyLineFigure;
import CH.ifa.draw.figures.SmoothPolyLineFigure;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.Storable;
import de.renew.draw.storables.ontology.StorableInput;
import de.renew.draw.storables.ontology.StorableOutput;
import de.renew.draw.ui.ontology.DrawingView;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;

/**
 * This is a test class for {@link CompositeFigure}.
 */
public class CompositeFigureTest {
    private int _xPoint;
    private int _yPoint;

    @BeforeEach
    public void setUp() {
        _xPoint = 10;
        _yPoint = 15;
    }

    @Test
    public void testAddFigureNotContained() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);

        // when
        Figure result = compositeFigure.add(figure);

        // then
        verify(compositeFigure).figureSetChanged();
        verify(figure).addToContainer(compositeFigure);
        assertThat(compositeFigure.fFigures).containsExactly(figure);
        assertThat(result).isSameAs(figure);
    }

    @Test
    public void testAddFigureContained() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(figure);

        // when
        Figure result = compositeFigure.add(figure);

        // then
        verify(compositeFigure).figureSetChanged();
        verify(figure, never()).addToContainer(compositeFigure);
        assertThat(compositeFigure.fFigures).containsExactly(figure);
        assertThat(result).isSameAs(figure);
    }

    @Test
    public void testAddAll() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        int initialSize = compositeFigure.fFigures.size();
        int numberOfCalls = fFigures.size();
        int sizeAfterAddAll = initialSize + numberOfCalls;

        // when
        compositeFigure.addAll(fFigures);

        // then
        verify(compositeFigure, times(numberOfCalls)).figureSetChanged();
        verify(figure1).addToContainer(compositeFigure);
        verify(figure2).addToContainer(compositeFigure);
        verify(figure3).addToContainer(compositeFigure);
        assertEquals(sizeAfterAddAll, compositeFigure.figureCount());
        assertThat(compositeFigure.fFigures).containsExactly(figure1, figure2, figure3);
    }

    @Test
    public void testRemoveFigureContained() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(figure);
        int initialSize = compositeFigure.fFigures.size();
        int sizeAfterRemoval = initialSize - 1;

        // when
        Figure result = compositeFigure.remove(figure);

        // then
        verify(compositeFigure).figureSetChanged();
        verify(figure).removeFromContainer(compositeFigure);
        verify(figure).release();
        assertEquals(sizeAfterRemoval, compositeFigure.figureCount());
        assertThat(compositeFigure.fFigures).doesNotContain(figure);
        assertThat(result).isSameAs(figure);
    }

    @Test
    public void testRemoveFigureNotContained() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        int initialSize = compositeFigure.fFigures.size();

        // when
        Figure result = compositeFigure.remove(figure);

        // then
        verify(compositeFigure).figureSetChanged();
        verify(figure, never()).removeFromContainer(compositeFigure);
        verify(figure, never()).release();
        assertEquals(initialSize, compositeFigure.figureCount());
        assertThat(result).isSameAs(figure);
    }

    @Test
    public void testRemoveAll() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        compositeFigure.add(figure1);
        compositeFigure.add(figure2);
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        int initialSize = compositeFigure.fFigures.size();
        int sizeAfterRemoval = initialSize - 2;
        int numberOfAdditionsAndRemovals = 5;

        // when
        compositeFigure.removeAll(fFigures);

        // then
        verify(compositeFigure, times(numberOfAdditionsAndRemovals)).figureSetChanged();
        verify(figure1).removeFromContainer(compositeFigure);
        verify(figure2).removeFromContainer(compositeFigure);
        verify(figure3, never()).removeFromContainer(compositeFigure);
        assertEquals(sizeAfterRemoval, compositeFigure.figureCount());
        assertThat(compositeFigure.fFigures).doesNotContain(figure1);
        assertThat(compositeFigure.fFigures).doesNotContain(figure2);
        assertThat(compositeFigure.fFigures).doesNotContain(figure3);
    }

    @Test
    public void testOrphan() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(figure);
        int initialSize = compositeFigure.fFigures.size();
        int sizeAfterOrphaning = initialSize - 1;

        // when
        Figure result = compositeFigure.orphan(figure);

        // then
        verify(compositeFigure).figureSetChanged();
        verify(figure, never()).removeFromContainer(any());
        assertEquals(sizeAfterOrphaning, compositeFigure.figureCount());
        assertThat(compositeFigure.fFigures).doesNotContain(figure);
        assertThat(result).isSameAs(figure);
    }

    @Test
    public void testOrphanAll() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        compositeFigure.add(figure1);
        compositeFigure.add(figure2);
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        int initialSize = compositeFigure.fFigures.size();
        int sizeAfterOrphaning = initialSize - 2;
        int numberOfOrphans = 3;
        int numberOfAdditions = 5;

        // when
        compositeFigure.orphanAll(fFigures);

        // then
        verify(compositeFigure, times(numberOfOrphans)).orphan(any(Figure.class));
        verify(compositeFigure, times(numberOfAdditions)).figureSetChanged();
        verify(figure1, never()).removeFromContainer(any());
        verify(figure2, never()).removeFromContainer(any());
        verify(figure3, never()).removeFromContainer(any());
        assertThat(compositeFigure.fFigures).doesNotContain(figure1);
        assertThat(compositeFigure.fFigures).doesNotContain(figure2);
        assertThat(compositeFigure.fFigures).doesNotContain(figure3);
        assertEquals(sizeAfterOrphaning, compositeFigure.figureCount());
    }

    @Test
    public void testReplaceFigureInCompositeFigure() {
        // given
        Figure oldFigure = mock(Figure.class);
        Figure newFigure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(oldFigure);
        int initialSize = compositeFigure.fFigures.size();

        // when
        compositeFigure.replace(oldFigure, newFigure);

        // then
        verify(newFigure).addToContainer(compositeFigure);
        verify(oldFigure).changed();
        verify(compositeFigure).figureSetChanged();
        assertEquals(initialSize, compositeFigure.figureCount());
        assertSame(newFigure, compositeFigure.fFigures.get(0));
        assertThat(compositeFigure.fFigures).doesNotContain(oldFigure);
    }

    @Test
    public void testReplaceFigureNotInCompositeFigure() {
        // given
        Figure oldFigure = mock(Figure.class);
        Figure newFigure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        int initialSize = compositeFigure.fFigures.size();

        // when
        compositeFigure.replace(oldFigure, newFigure);

        // then
        verify(newFigure, never()).addToContainer(compositeFigure);
        verify(oldFigure, never()).changed();
        verify(compositeFigure).figureSetChanged();
        assertEquals(initialSize, compositeFigure.figureCount());
        assertThat(compositeFigure.fFigures).doesNotContain(oldFigure);
        assertThat(compositeFigure.fFigures).doesNotContain(newFigure);
    }

    @Test
    public void testSendToBackFigureContained() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(figure1);
        compositeFigure.fFigures.add(figure2);
        compositeFigure.fFigures.add(figure3);

        // when
        compositeFigure.sendToBack(figure2);

        // then
        verify(figure2).changed();
        assertThat(compositeFigure.fFigures).containsExactly(figure2, figure1, figure3);
    }

    @Test
    public void testSendToBackFigureNotContained() {
        // given
        Figure notInFigures = mock(Figure.class);
        Figure inFigures = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(inFigures);
        int initialSize = compositeFigure.fFigures.size();

        // when
        compositeFigure.sendToBack(notInFigures);

        // then
        verify(notInFigures, never()).changed();
        assertEquals(initialSize, compositeFigure.figureCount());
    }

    @Test
    public void testBringToFrontFigureContained() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(figure1);
        compositeFigure.fFigures.add(figure2);

        // when
        compositeFigure.bringToFront(figure1);

        // then
        verify(figure1).changed();
        assertThat(compositeFigure.fFigures).containsExactly(figure2, figure1);
    }

    @Test
    public void testBringToFrontFigureNotContained() {
        // given
        Figure notInFigures = mock(Figure.class);
        Figure inFigures = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        compositeFigure.fFigures.add(inFigures);
        int initialSize = compositeFigure.fFigures.size();

        // when
        compositeFigure.bringToFront(notInFigures);

        // then
        verify(notInFigures, never()).changed();
        assertEquals(initialSize, compositeFigure.figureCount());
    }

    @Test
    public void testFindFigureXYFigureEnumeratorNotEmpty() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.containsPoint(_xPoint, _yPoint)).thenReturn(true);
        when(figure2.containsPoint(anyInt(), anyInt())).thenReturn(false);
        when(figure3.containsPoint(anyInt(), anyInt())).thenReturn(false);

        // when
        Figure result = compositeFigure.findFigure(_xPoint, _yPoint);

        // then
        assertEquals(figure1, result);
        assertTrue(figure1.containsPoint(_xPoint, _yPoint));
        assertFalse(figure2.containsPoint(anyInt(), anyInt()));
        assertFalse(figure3.containsPoint(anyInt(), anyInt()));
    }

    @Test
    public void testFindFigureXYFigureEnumeratorEmpty() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure.containsPoint(_xPoint, _yPoint)).thenReturn(false);

        // when
        Figure result = compositeFigure.findFigure(_xPoint, _yPoint);

        // then
        verify(figure).containsPoint(_xPoint, _yPoint);
        assertNull(result);
    }

    @Test
    public void testFindFigureRectfigureEnumeratorNotEmpty() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        Rectangle inputRect = new Rectangle(5, 5, 10, 10);
        Rectangle box1 = new Rectangle(0, 0, 3, 3);
        Rectangle box2 = new Rectangle(4, 4, 1, 1); // Only intersects with the inputRect after grow(1,1) is called
        Rectangle box3 = new Rectangle(8, 8, 12, 12);
        when(figure1.displayBox()).thenReturn(box1);
        when(figure2.displayBox()).thenReturn(box2);
        when(figure3.displayBox()).thenReturn(box3);

        // when
        Figure result = compositeFigure.findFigure(inputRect);

        // then
        verify(figure1).displayBox();
        verify(figure2).displayBox();
        assertEquals(figure2, result);
    }

    @Test
    public void testFindFigureRectFigureEnumeratorEmpty() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        Rectangle inputRect = new Rectangle(0, 0, 5, 5);
        Rectangle box = new Rectangle(100, 100, 10, 10);
        when(figure.displayBox()).thenReturn(box);

        // when
        Figure result = compositeFigure.findFigure(inputRect);

        // then
        verify(figure).displayBox();
        assertNull(result);
    }

    @Test
    public void testFindFigureRectWithoutNotNull() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure withoutFigure = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(withoutFigure);
        fFigures.add(figure2);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        Rectangle inputRect = new Rectangle(5, 5, 10, 10);
        Rectangle box1 = new Rectangle(0, 0, 3, 3);
        Rectangle box2 = new Rectangle(4, 4, 6, 6);
        Rectangle box3 = new Rectangle(4, 4, 1, 1); // Only intersects with the inputRect after grow(1,1) is called
        when(figure1.displayBox()).thenReturn(box1);
        when(withoutFigure.displayBox()).thenReturn(box2);
        when(figure2.displayBox()).thenReturn(box3);
        when(figure1.includes(withoutFigure)).thenReturn(false);
        when(withoutFigure.includes(withoutFigure)).thenReturn(true);
        when(figure2.includes(withoutFigure)).thenReturn(false);

        // when
        Figure result = compositeFigure.findFigure(inputRect, withoutFigure);

        // then
        verify(figure1).displayBox();
        verify(withoutFigure).displayBox();
        verify(figure2).displayBox();
        verify(figure1, never()).includes(withoutFigure);
        verify(withoutFigure).includes(withoutFigure);
        verify(figure2).includes(withoutFigure);
        assertEquals(figure2, result);
    }

    @Test
    public void testFindFigureRectWithoutIsNull() {
        // given
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Rectangle inputRect = new Rectangle(5, 5, 10, 10);

        // when
        Figure result = compositeFigure.findFigure(inputRect, null);

        // then
        assertNull(result);
    }

    @Test
    public void testFindFigureRectWithoutReturnNull() {
        // given
        Figure figure = mock(Figure.class);
        Figure withoutFigure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        fFigures.add(withoutFigure);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        Rectangle inputRect = new Rectangle(5, 5, 10, 10);
        Rectangle box = new Rectangle(4, 4, 6, 6);
        when(figure.displayBox()).thenReturn(box);
        when(withoutFigure.displayBox()).thenReturn(box);
        when(figure.includes(withoutFigure)).thenReturn(true);
        when(withoutFigure.includes(withoutFigure)).thenReturn(true);

        // when
        Figure result = compositeFigure.findFigure(inputRect, withoutFigure);

        // then
        verify(figure).displayBox();
        verify(figure).includes(withoutFigure);
        assertNull(result);
    }

    @Test
    public void testFindFigureWithoutReturnFigure() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure withoutFigure = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(withoutFigure);
        fFigures.add(figure2);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.containsPoint(_xPoint, _yPoint)).thenReturn(false);
        when(withoutFigure.containsPoint(_xPoint, _yPoint)).thenReturn(true);
        when(figure2.containsPoint(_xPoint, _yPoint)).thenReturn(true);
        when(withoutFigure.includes(withoutFigure)).thenReturn(true);
        when(figure2.includes(withoutFigure)).thenReturn(false);

        // when
        Figure result = compositeFigure.findFigureWithout(_xPoint, _yPoint, withoutFigure);

        // then
        verify(figure1).containsPoint(_xPoint, _yPoint);
        verify(withoutFigure).containsPoint(_xPoint, _yPoint);
        verify(figure2).containsPoint(_xPoint, _yPoint);
        verify(withoutFigure).includes(withoutFigure);
        verify(figure2).includes(withoutFigure);
        assertEquals(figure2, result);
    }

    @Test
    public void testFindFigureWithoutIsNull() {
        // given
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        doReturn(mock(Figure.class)).when(compositeFigure).findFigure(_xPoint, _yPoint);

        // when
        compositeFigure.findFigureWithout(_xPoint, _yPoint, null);

        // then
        verify(compositeFigure).findFigure(_xPoint, _yPoint);
        assertNotNull(compositeFigure);
    }

    @Test
    public void testFindFigureWithoutReturnNull() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure without = mock(Figure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        fFigures.add(without);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure.containsPoint(_xPoint, _yPoint)).thenReturn(true);
        when(figure.includes(without)).thenReturn(true);

        // when
        Figure result = compositeFigure.findFigureWithout(_xPoint, _yPoint, without);

        // then
        verify(figure).containsPoint(_xPoint, _yPoint);
        verify(figure).includes(without);
        assertNull(result);
    }

    @Test
    public void testFindFigureInsideReturnFigure() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        Figure figureToReturn = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.findFigureInside(_xPoint, _yPoint)).thenReturn(null);
        when(figure2.findFigureInside(_xPoint, _yPoint)).thenReturn(figureToReturn);
        when(figure3.findFigureInside(_xPoint, _yPoint)).thenReturn(null);

        // when
        Figure result = compositeFigure.findFigureInside(_xPoint, _yPoint);

        // then
        verify(figure1).findFigureInside(_xPoint, _yPoint);
        verify(figure2).findFigureInside(_xPoint, _yPoint);
        verify(figure3, never()).findFigureInside(_xPoint, _yPoint);
        assertEquals(figureToReturn, result);
    }

    @Test
    public void testFindFigureInsideReturnNull() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure.findFigureInside(_xPoint, _yPoint)).thenReturn(null);

        // when
        Figure result = compositeFigure.findFigureInside(_xPoint, _yPoint);

        // then
        verify(figure).findFigureInside(_xPoint, _yPoint);
        assertNull(result);
    }

    @Test
    public void testFindFigureInsideWithoutReturnFound() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure withoutFigure = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figureToReturn = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(withoutFigure);
        fFigures.add(figure2);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.findFigureInside(_xPoint, _yPoint)).thenReturn(null);
        when(figure2.findFigureInside(_xPoint, _yPoint)).thenReturn(figureToReturn);

        // when
        Figure result = compositeFigure.findFigureInsideWithout(_xPoint, _yPoint, withoutFigure);

        // then
        assertEquals(figureToReturn, result);
    }

    @Test
    public void testFindFigureInsideWithoutNotInFigure() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        Figure figure3 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure without = mock(Figure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(figure2);
        fFigures.add(figure3);
        fFigures.add(without);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.findFigureInside(_xPoint, _yPoint)).thenReturn(null);
        when(figure2.findFigureInside(_xPoint, _yPoint)).thenReturn(null);

        // when
        Figure result = compositeFigure.findFigureInsideWithout(_xPoint, _yPoint, without);

        // then
        assertNull(result);
    }

    @Test
    public void testFindFigureInsideWithoutReturnNull() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure withoutFigure = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(withoutFigure);
        fFigures.add(figure2);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.findFigureInside(_xPoint, _yPoint)).thenReturn(null);
        when(withoutFigure.findFigureInside(_xPoint, _yPoint)).thenReturn(null);
        when(figure2.findFigureInside(_xPoint, _yPoint)).thenReturn(null);

        // when
        Figure result = compositeFigure.findFigureInsideWithout(_xPoint, _yPoint, withoutFigure);

        // then
        assertNull(result);
    }

    @Test
    public void testFindFigureInsideWithoutSkipsWithout() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure withoutFigure = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(withoutFigure);
        fFigures.add(figure2);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figuresReverse();
        when(figure1.findFigureInside(_xPoint, _yPoint)).thenReturn(null);
        when(withoutFigure.findFigureInside(_xPoint, _yPoint)).thenReturn(withoutFigure);
        when(figure2.findFigureInside(_xPoint, _yPoint)).thenReturn(null);

        // when
        Figure result = compositeFigure.findFigureInsideWithout(_xPoint, _yPoint, withoutFigure);

        // then
        assertNull(result);
    }



    @Test
    public void testIncludesSuperIncludesFigure() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        doReturn(true).when(compositeFigure).includes(figure);

        // when
        boolean result = compositeFigure.includes(figure);

        //then
        assertTrue(result);
    }

    @Test
    public void testIncludesSuperDoesntIncludeFigureReturnTrue() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure childFigure = mock(Figure.class);
        compositeFigure.add(figure);
        compositeFigure.add(childFigure);
        when(childFigure.includes(figure)).thenReturn(true);

        // when
        boolean result = compositeFigure.includes(figure);

        //then
        assertTrue(result);
    }

    @Test
    public void testIncludesSuperDoesntIncludeFigureReturnFalse() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure childFigure = mock(Figure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        fFigures.add(childFigure);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figures();
        when(childFigure.includes(figure)).thenReturn(false);

        // when
        boolean result = compositeFigure.includes(figure);

        //then
        assertFalse(result);
    }

    @Test
    public void testIncludesFigureNotIncluded() {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure childFigure = mock(Figure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure);
        fFigures.add(childFigure);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figures();
        when(childFigure.includes(figure)).thenReturn(false);

        // when
        boolean result = compositeFigure.includes(figure);

        //then
        assertFalse(result);
    }

    @Test
    public void testRelease() {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Vector<Figure> fFigures = new Vector<>();
        fFigures.add(figure1);
        fFigures.add(figure2);
        FigureEnumerator figureEnumerator = new FigureEnumerator(fFigures);
        doReturn(figureEnumerator).when(compositeFigure).figures();

        // when
        compositeFigure.release();

        // then
        verify(compositeFigure).release();
        verify(figure1).release();
        verify(figure2).release();
    }

    @Test
    public void testWrite() {
        // given
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        StorableOutput output = mock(StorableOutput.class);
        Figure normalFigure = mock(Figure.class, withSettings().extraInterfaces(Storable.class));
        SmoothPolyLineFigure smoothFigure = new SmoothPolyLineFigure(1.0);
        int numberOfFigures = 2;
        smoothFigure.addPoint(_xPoint, _yPoint);
        smoothFigure.addPoint(_xPoint, _yPoint);
        compositeFigure.fFigures.add(normalFigure);
        compositeFigure.fFigures.add(smoothFigure);

        // when
        compositeFigure.write(output);

        // then
        verify(output).writeInt(numberOfFigures);
        verify(output).writeStorable(normalFigure);
        ArgumentCaptor<Storable> storableCaptor = ArgumentCaptor.forClass(Storable.class);
        verify(output, times(numberOfFigures)).writeStorable(storableCaptor.capture());
        Storable secondWritten = storableCaptor.getAllValues().get(1);
        assertThat(secondWritten).isInstanceOf(PolyLineFigure.class);
        PolyLineFigure poly = (PolyLineFigure) secondWritten;
        assertEquals(PolyLineFigure.BSPLINE_SHAPE, poly.getAttribute("LineShape"));
    }

    @Test
    public void testReadNoException() throws IOException {
        // given
        Figure figure1 = mock(Figure.class);
        Figure figure2 = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        StorableInput input = mock(StorableInput.class);
        int numberOfFigures = 2;
        when(input.readInt()).thenReturn(numberOfFigures);
        when(input.readStorable()).thenReturn(figure1, figure2);

        // when
        compositeFigure.read(input);

        // then
        verify(input, times(numberOfFigures)).readStorable();
        assertEquals(numberOfFigures, compositeFigure.figureCount());
    }

    @Test
    public void testReadExceptionThrown() throws IOException {
        // given
        Figure figure = mock(Figure.class);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        StorableInput input = mock(StorableInput.class);
        int numberOfFigures = 2;
        when(input.readInt()).thenReturn(2);
        when(input.readStorable()).thenReturn(figure)
            .thenThrow(new IOException("Error while reading"));

        // when
        compositeFigure.read(input);

        // then
        verify(input, times(numberOfFigures)).readStorable();
        verify(compositeFigure).add(figure);
        assertEquals(1, compositeFigure.figureCount());
    }

    @Test
    public void testInspectReturnTrue() {
        // given
        Point clickPoint = new Point(10, 20);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure figure = mock(Figure.class);
        DrawingView view = mock(DrawingView.class);
        when(view.lastClick()).thenReturn(clickPoint);
        doReturn(figure).when(compositeFigure).findFigure(clickPoint.x, clickPoint.y);
        when(figure.inspect(view, true)).thenReturn(true);

        // when
        boolean result = compositeFigure.inspect(view, true);

        // then
        verify(figure).inspect(view, true);
        assertTrue(result);
    }

    @Test
    public void testInspectReturnSuperInspect() {
        // given
        Point clickPoint = new Point(10, 20);
        CompositeFigure compositeFigure = spy(CompositeFigure.class);
        Figure figure = mock(Figure.class);
        DrawingView view = mock(DrawingView.class);
        when(view.lastClick()).thenReturn(clickPoint);
        doReturn(true).when(compositeFigure).inspect(view, false);
        doReturn(null).when(compositeFigure).findFigure(clickPoint.x, clickPoint.y);
        when(figure.inspect(view, false)).thenReturn(false);

        // when
        boolean result = compositeFigure.inspect(view, false);

        // then
        assertTrue(result);
    }
}