package org.bukkit;

import static org.bukkit.support.MatcherAssert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.jupiter.api.Assertions.*;
import com.google.common.collect.Lists;
import java.util.Collection;
import org.junit.jupiter.api.Test;

public class NoteTest {
    @Test
    public void getToneByData() {
        for (Note.Tone tone : Note.Tone.values()) {
            assertThat(Note.Tone.getById(tone.getId()), is(tone));
        }
    }

    @Test
    public void verifySharpedData() {
        for (Note.Tone tone : Note.Tone.values()) {
            if (!tone.isSharpable()) return;

            assertFalse(tone.isSharped(tone.getId(false)));
            assertTrue(tone.isSharped(tone.getId(true)));
        }
    }

    @Test
    public void verifyUnknownToneData() {
        Collection<Byte> tones = Lists.newArrayList();
        for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
            tones.add((byte) i);
        }

        for (Note.Tone tone : Note.Tone.values()) {
            if (tone.isSharpable()) tones.remove(tone.getId(true));
            tones.remove(tone.getId());
        }

        for (Byte data : tones) {
            assertThat(Note.Tone.getById(data), is(nullValue()));

            for (Note.Tone tone : Note.Tone.values()) {
                try {
                    tone.isSharped(data);

                    fail(data + " should throw IllegalArgumentException");
                } catch (IllegalArgumentException e) {
                    assertNotNull(e);
                }
            }
        }
    }

    @Test
    public void createNoteBelowMin() {
        assertThrows(IllegalArgumentException.class, () -> new Note((byte) -1));
    }

    @Test
    public void createNoteAboveMax() {
        assertThrows(IllegalArgumentException.class, () -> new Note((byte) 25));
    }

    @Test
    public void createNoteOctaveBelowMax() {
        assertThrows(IllegalArgumentException.class, () -> new Note((byte) -1, Note.Tone.A, true));
    }

    @Test
    public void createNoteOctaveAboveMax() {
        assertThrows(IllegalArgumentException.class, () -> new Note((byte) 3, Note.Tone.A, true));
    }

    @Test
    public void createNoteOctaveNonSharpable() {
        Note note = new Note((byte) 0, Note.Tone.B, true);
        assertFalse(note.isSharped());
        assertThat(note.getTone(), is(Note.Tone.C));
    }

    @Test
    public void createNoteFlat() {
        Note note = Note.flat(0, Note.Tone.D);
        assertTrue(note.isSharped());
        assertThat(note.getTone(), is(Note.Tone.C));
    }

    @Test
    public void createNoteFlatNonFlattenable() {
        Note note = Note.flat(0, Note.Tone.C);
        assertFalse(note.isSharped());
        assertThat(note.getTone(), is(Note.Tone.B));
    }

    @Test
    public void testFlatWrapping() {
        Note note = Note.flat(1, Note.Tone.G);
        assertTrue(note.isSharped());
        assertThat(note.getTone(), is(Note.Tone.F));
    }

    @Test
    public void testFlatWrapping2() {
        Note note = new Note(1, Note.Tone.G, false).flattened();
        assertTrue(note.isSharped());
        assertThat(note.getTone(), is(Note.Tone.F));
    }

    @Test
    public void testSharpWrapping() {
        Note note = new Note(1, Note.Tone.F, false).sharped();
        assertTrue(note.isSharped());
        assertThat(note.getTone(), is(Note.Tone.F));
        assertEquals(note.getOctave(), 2);
    }

    @Test
    public void testSharpWrapping2() {
        assertThrows(IllegalArgumentException.class, () -> new Note(2, Note.Tone.F, true).sharped());
    }

    @Test
    public void testHighest() {
        Note note = new Note(2, Note.Tone.F, true);
        assertEquals(note.getId(), (byte) 24);
    }

    @Test
    public void testLowest() {
        Note note = new Note(0, Note.Tone.F, true);
        assertEquals(note.getId(), (byte) 0);
    }

    @Test
    public void doo() {
        for (int i = 1; i <= 24; i++) {
            Note note = new Note((byte) i);
            int octave = note.getOctave();
            Note.Tone tone = note.getTone();
            boolean sharped = note.isSharped();

            Note newNote = new Note(octave, tone, sharped);
            assertThat(newNote, is(note));
            assertThat(newNote.getId(), is(note.getId()));
        }
    }
}
