/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.reflection.impl;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import static net.ranides.assira.reflection.IAttribute.*;
import net.ranides.assira.reflection.IClass;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.reflection.IAttribute;
import net.ranides.assira.reflection.IClasses;
import net.ranides.assira.reflection.IContext;
import net.ranides.assira.reflection.mockup.ForBeanModel;
import org.junit.Test;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class AClassTest {
    
    public AClassTest() {
    }

    @Test
    public void testConstructors() {
        List<String> exp1 = Arrays.asList(
            "net.ranides.assira.reflection.impl.AClassTest$A[]"
        );
        List<String> exp2 = Arrays.asList(
            "net.ranides.assira.reflection.impl.AClassTest$B[int]",
            "net.ranides.assira.reflection.impl.AClassTest$B[int, int]",
            "net.ranides.assira.reflection.impl.AClassTest$B[]"
        );
        
        List<String> list1 = IClass.typeinfo(A.class)
            .constructors()
            .require(DECLARED)
            .accepts()
            .list(c -> c.name()+c.arguments().types());
        assertEquivalent(exp1, list1);
        
        List<String> list2 = IClass.typeinfo(B.class)
            .constructors()
            .require(DECLARED)
            .list(c -> c.name()+c.arguments().types());
        assertEquivalent(exp2, list2);

    }
    
    @Test
    public void testConstruct() {
        IClass<?> ic = IClass.typeinfo(B.class);
        
        assertEquals("B{777:777}", ic.construct().toString());
        assertEquals("B{3:3}", ic.construct(3).toString());
        assertEquals("B{5:4}", ic.construct(5,4).toString());
    }
    
    @Test
    public void testNULL() {
        IClass<?> ic = IClass.NULL;
        
        assertSame(IContext.DEFAULT, ic.context());
        assertEquals(Collections.emptyList(), ic.params());
        assertEquals(Collections.emptyList(), ic.fields().list());
        assertEquals(Collections.emptyList(), ic.constructors().list());
        assertEquals(Collections.emptyList(), ic.methods().list());
        assertEquals(Collections.emptyList(), ic.parents().list());
        assertEquals(Collections.emptyList(), ic.interfaces().list());
        assertEquals(Collections.emptyList(), ic.inner().list());
        assertEquals(Collections.emptyList(), ic.annotations().list());
        assertEquals(collect(FINAL, NATIVE, PUBLIC), ic.attributes());
        
        assertEquals(false, ic.attributes().contains(PRIMITIVE));
        assertEquals(true, ic.isInstance(null));
        assertEquals(true, ic.isInstance(55));
        assertEquals(true, ic.isInstance("hello"));
        
        assertThrows(NoSuchElementException.class, ()->{
            ic.method("hello").get();
        });
        assertThrows(NoSuchElementException.class, ()->{
            ic.parent();
        });
        assertThrows(NoSuchElementException.class, ()->{
            ic.outer();
        });
        assertThrows(NoSuchElementException.class, ()->{
            ic.construct();
        });
        assertThrows(NoSuchElementException.class, ()->{
            ic.construct(1,2,3);
        });
        assertThrows(UnsupportedOperationException.class, ()->{
            ic.reflective();
        });
        assertThrows(UnsupportedOperationException.class, ()->{
            ic.raw();
        });
    }
    
    @Test
    public void testCollect() {
        assertEquals(IClasses.typeinfo(int.class).list(), IClass.typeinfo(int.class).collect().list());
        assertNotEquals(IClasses.typeinfo(float.class).list(), IClass.typeinfo(int.class).collect().list());
    }
    
    @Test
    public void testVoid() {
        Set<IAttribute> exp1 = collect(PUBLIC, FINAL, ABSTRACT, DECLARED, PRIMITIVE, VOID);
        Set<IAttribute> exp2 = collect(PUBLIC, FINAL, DECLARED, BOXED, VOID);
        Set<IAttribute> exp3 = collect(PUBLIC, FINAL, ABSTRACT, DECLARED, PRIMITIVE, NUMBER, INTEGER);
        Set<IAttribute> exp4 = collect(PUBLIC, FINAL, ABSTRACT, DECLARED, PRIMITIVE, VOID);
        
        assertEquals(exp1, IClass.typeinfo(void.class).attributes());
        assertEquals(exp2, IClass.typeinfo(Void.class).attributes());
        assertEquals(exp3, IClass.typeinfo(ForBeanModel.class).method("getArea").get().returns().attributes());
        assertEquals(exp4, IClass.typeinfo(ForBeanModel.class).method("setList").get().returns().attributes());
    }
    
    private static class A { }
    
    private static class B { 
        
        public final int x;
        public final int y;
        
        public B(int x) {
            this.x = x;
            this.y = x;
        }
        
        protected B(int x, int y) { 
            this.x = x;
            this.y = y;
        }
        
        private B() {
            this.x = 777;
            this.y = 777;
        }

        @Override
        public String toString() {
            return "B{" + x + ":" + y + '}';
        }

    }
    
}
