package de.renew.formalism.java;

import java.util.Collection;

import org.junit.jupiter.api.Test;

import de.renew.expression.ConstantExpression;
import de.renew.unify.List;
import de.renew.unify.Tuple;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class VariableSuggestionTest {

    @Test
    public void testConstructor() {
        // given
        VariableSuggestion suggestionA = new VariableSuggestion("number", Integer.class, false);
        VariableSuggestion suggestionB =
            new VariableSuggestion("suggestion", VariableSuggestion.class, true);

        // when
        boolean editDesiredA = suggestionA.isEditDesired();
        boolean isImportNeededA = suggestionA.isImportNeeded();
        Class<?> typeA = suggestionA.getType();
        String nameA = suggestionA.getName();
        String typeNameA = suggestionA.getTypeName();

        boolean editDesiredB = suggestionB.isEditDesired();
        boolean isImportNeededB = suggestionB.isImportNeeded();
        Class<?> typeB = suggestionB.getType();
        String nameB = suggestionB.getName();
        String typeNameB = suggestionB.getTypeName();

        // then
        assertFalse(editDesiredA);
        assertFalse(isImportNeededA);
        assertEquals(Integer.class, typeA);
        assertEquals("number", nameA);
        assertEquals("int", typeNameA);


        assertFalse(editDesiredB);
        assertTrue(isImportNeededB);
        assertEquals(VariableSuggestion.class, typeB);
        assertEquals("suggestion", nameB);
        assertEquals("VariableSuggestion", typeNameB);
    }

    @Test
    public void testGetTypeName() {
        assertEquals("byte", new VariableSuggestion("x", Byte.class, false).getTypeName());
        assertEquals("short", new VariableSuggestion("x", Short.class, false).getTypeName());
        assertEquals("int", new VariableSuggestion("x", Integer.class, false).getTypeName());
        assertEquals("long", new VariableSuggestion("x", Long.class, false).getTypeName());
        assertEquals("float", new VariableSuggestion("x", Float.class, false).getTypeName());
        assertEquals("double", new VariableSuggestion("x", Double.class, false).getTypeName());
        assertEquals("boolean", new VariableSuggestion("x", Boolean.class, false).getTypeName());
        assertEquals("char", new VariableSuggestion("x", Character.class, false).getTypeName());
        assertEquals(
            "VariableSuggestion",
            new VariableSuggestion("x", VariableSuggestion.class, false).getTypeName());
    }

    @Test
    public void testEquals() {
        // given
        VariableSuggestion a = new VariableSuggestion("x", Byte.class, false);
        VariableSuggestion b = new VariableSuggestion("x", Byte.class, false);
        VariableSuggestion c = new VariableSuggestion("x", Integer.class, false);
        VariableSuggestion d = new VariableSuggestion("y", Byte.class, false);
        VariableSuggestion e = new VariableSuggestion("x", Byte.class, true);

        // then
        assertEquals(a, a);
        assertEquals(a, b);
        assertNotEquals(a, c);
        assertEquals(a, d);
        assertEquals(a, e);
        assertNotEquals(c, e);
        assertNotEquals(c, d);
        assertEquals(d, e);
        assertNotEquals(a, null);
        assertNotEquals(b, null);
        assertNotEquals(c, null);
        assertNotEquals(d, null);
        assertNotEquals(e, null);
        assertNotEquals(a, new Object());
        assertNotEquals(b, new Object());
        assertNotEquals(c, new Object());
        assertNotEquals(d, new Object());
        assertNotEquals(e, new Object());
    }


    @Test
    public void testToString() {
        // given
        VariableSuggestion suggestionA = new VariableSuggestion("number", Integer.class, false);
        VariableSuggestion suggestionB =
            new VariableSuggestion("suggestion", VariableSuggestion.class, true);

        // when
        String stringA = suggestionA.toString();
        String stringB = suggestionB.toString();

        // then
        assertEquals(
            "<html>Declare number as int <font color=gray>- java.lang</font></html>", stringA);
        assertEquals(
            "<html>Declare suggestion as VariableSuggestion <font color=gray>- de.renew.formalism.java</font></html>",
            stringB);
    }

    @Test
    public void testSuggestImport() {
        // given
        TypedExpression expr =
            new TypedExpression(Integer.class, new ConstantExpression(Integer.class, 42));
        ParsedDeclarationNode declNode = new ParsedDeclarationNode();
        PrimaryPart primaryPart = new PrimaryPart(PrimaryPart.NAME, "foo", new Token());

        // when
        Collection<VariableSuggestion> suggestions =
            VariableSuggestion.suggest(primaryPart, expr, declNode);

        // then
        assertEquals(6, suggestions.size());
        assertTrue(suggestions.contains(new VariableSuggestion("foo", Integer.class, false)));
        assertTrue(suggestions.contains(new VariableSuggestion("foo", Float.class, false)));
        assertTrue(suggestions.contains(new VariableSuggestion("foo", List.class, false)));
        assertTrue(suggestions.contains(new VariableSuggestion("foo", Object.class, false)));
        assertTrue(suggestions.contains(new VariableSuggestion("foo", Tuple.class, false)));
    }
}
