package net.ranides.assira.system.impl;

import lombok.Data;
import net.ranides.assira.annotations.Meta;
import net.ranides.assira.collection.maps.OpenMap;
import net.ranides.assira.generic.TypeToken;
import net.ranides.assira.io.IOEvent;
import net.ranides.assira.reflection.IClass;
import org.junit.Test;

import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

public class HostSimpleContextTest {

    @Test
    public void testNull() {
        HostSimpleContext ctx = new HostSimpleContext();
        ctx.registerPrototype("list", IClass.typeinfo(ArrayList.class));
        ctx.registerPrototype("list", IClass.typeinfo(LinkedList.class));
        ctx.registerPrototype("map", IClass.typeinfo(OpenMap.class));

        ctx.registerSingleton("font", new Font("Arial", Font.PLAIN, 16));
        ctx.registerSingleton("event", IOEvent.close(null));

        assertNotNull(ctx.getBean(IClass.typeinfo(RandomAccess.class)));
        assertNotNull(ctx.getBean(IClass.typeinfo(ArrayList.class)));
        assertNotNull(ctx.getBean(IClass.typeinfo(Map.class)));
        assertNotNull(ctx.getBean(IClass.typeinfo(OpenMap.class)));

        assertNotNull(ctx.getBean(IClass.typeinfo(Font.class)));
        assertNotNull(ctx.getBean(IClass.typeinfo(IOEvent.class)));

        assertThrows(NoSuchElementException.class, () -> {
            ctx.getBean(IClass.typeinfo(List.class));
        });
        assertThrows(NoSuchElementException.class, () -> {
            ctx.getBean(IClass.typeinfo(List.class));
        });
        assertThrows(NoSuchElementException.class, () -> {
            ctx.getBean(IClass.typeinfo(String.class));
        });

        assertNotNull(ctx.getBean("map"));
        assertNotNull(ctx.getBean("font"));
        assertNotNull(ctx.getBean("event"));

//        assertThrows(NoSuchElementException.class, () -> {
//            ctx.getBean("list");
//        });
        assertThrows(NoSuchElementException.class, () -> {
            ctx.getBean("invalid");
        });

    }

    @Test
    public void testInstance() {
        HostSimpleContext ctx = new HostSimpleContext();
        ctx.registerPrototype("list", IClass.typeinfo(ArrayList.class));
        ctx.registerPrototype("list", IClass.typeinfo(LinkedList.class));
        ctx.registerPrototype("map", IClass.typeinfo(OpenMap.class));
        ctx.registerSingleton("font", new Font("Arial", Font.PLAIN, 16));
        ctx.registerSingleton("event", IOEvent.close(null));

        assertTrue(ctx.getBean(IClass.typeinfo(RandomAccess.class)) instanceof ArrayList<?>);
        assertEquals(0, ctx.getBean(IClass.typeinfo(ArrayList.class)).size());
        assertEquals(0, ctx.getBean(IClass.typeinfo(Map.class)).entrySet().size() );
        assertEquals(0, ctx.getBean(IClass.typeinfo(OpenMap.class)).size() );

        assertEquals(16, ctx.getBean(IClass.typeinfo(Font.class)).getSize());
        assertTrue(ctx.getBean(IClass.typeinfo(IOEvent.class)) instanceof IOEvent.Close);


        assertTrue(ctx.getBean("map") instanceof Map<?,?>);
        assertTrue(ctx.getBean("font") instanceof Font);
        assertTrue(ctx.getBean("event") instanceof IOEvent.Close);
    }

    @Test
    public void testIdentity() {
        HostSimpleContext ctx = new HostSimpleContext();

        ctx.registerPrototype("list", IClass.typeinfo(ArrayList.class));
        ctx.registerSingleton("event", IOEvent.close(null));

        java.util.List<Number> list1 = ctx.getBean(IClass.typeinfo(List.class));
        java.util.List<Number> list2 = ctx.getBean(new TypeToken<List<Number>>() {});
        list1.add(15);
        list1.add(9);
        list2.add(12);
        list2.add(8);
        assertEquals(Arrays.asList(15, 9), list1);
        assertEquals(Arrays.asList(12, 8), list2);
    }

    @Test
    public void testArguments() {
        HostSimpleContext ctx = new HostSimpleContext();

        ctx.registerSingleton("one", new MyBean1());
        ctx.registerPrototype("two", MyBean2.class);
        ctx.registerPrototype("tre", MyBean3.class);

        assertEquals("HostSimpleContextTest.MyBean3(count=3)", ctx.getBean("tre").toString());
        assertEquals(3, ctx.getBean(MyBean3.class).getCount());
        assertEquals(1, ctx.getBean(MyBean1.class).getA());
        assertEquals(2, ctx.getBean(MyBean2.class).getB());

        ctx.getBean(MyBean1.class).setA(8);
        ctx.getBean(MyBean2.class).setB(9);

        assertEquals(10, ctx.getBean(MyBean3.class).getCount());
    }

    @Data
    public static class MyBean1 {
        private int a = 1;
    }

    @Data
    public static class MyBean2 {
        private int b = 2;
    }

    @Data
    public static class MyBean3 {

        private final int count;

        @Meta.Dynamic
        public MyBean3(MyBean1 first, MyBean2 second) {
            count = first.a + second.b;
        }

        @Meta.Dynamic
        public MyBean3(MyBean2 second) {
            count = second.b;
        }
    }
}