/*
 * @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.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import net.ranides.assira.annotations.Meta;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.mockup.ForConstructor;
import org.junit.Test;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.reflection.IArguments;
import net.ranides.assira.reflection.IClasses;
import net.ranides.assira.reflection.IMethod;
import net.ranides.assira.reflection.IMethods;
import net.ranides.assira.reflection.mockup.ForConstructor.Record;
import net.ranides.assira.reflection.util.AnnotationUtils;
import org.junit.Ignore;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class RConstructorTest {
    
    IClass<?> ic = IClass.typeinfo(ForConstructor.Record.class);
            
    @Test
    public void testParams() {
        String cn = "net.ranides.assira.reflection.mockup.ForConstructor$Record";
        List<String> exp = Arrays.asList(
            MessageFormat.format("{0} {0}.{0}(R param1, R param2)", cn),
            MessageFormat.format("{0} {0}.{0}(int x, int y, int z)", cn),
            MessageFormat.format("{0} {0}.{0}(java.lang.String param)", cn), 
            MessageFormat.format("{0} {0}.{0}()", cn)
        );
        assertEquivalent(exp, ic.constructors().list(m -> m.toString()));
        assertEquivalent(Arrays.asList("[R]", "[]", "[]", "[]"), ic.constructors().list(m -> m.params().toString()));
    }
    
    @Test
    public void testTypeInfo() throws Exception {
        IMethod c1 = ic.constructors().require(c -> c.arguments().count()==3).first().get();
        assertThrows(UnsupportedOperationException.class, ()->{
            c1.reflective();
        });
        
        Constructor<ForConstructor.Record> rc1 = ForConstructor.Record.class.getConstructor(int.class, int.class, int.class);
        IMethod c2 = IMethod.typeinfo(rc1);
        assertEquals(c1, c2);
    }
    
    @Test
    public void testApply() {
        ForConstructor.Record that = new ForConstructor.Record();
        
        IMethod c1 = ic.constructors().require(0).first().get();
        IMethod c2 = ic.constructors().require(2).first().get();
        IMethod c3 = ic.constructors().require(3).first().get();

        assertEquals("Record{0:0:0}", c1.apply().toString());
        assertEquals("Record{0:0:0}", c1.apply(null).toString());
        assertThrows(IllegalArgumentException.class, ()->{
            c1.apply(that);
        });
        
        assertEquals("Record{5:6:11}", c2.apply(null, 5.1, 6.4).toString());
        assertThrows(IllegalArgumentException.class, ()->{
            c2.apply(that, 5.1, 6.4);
        });
        
        assertEquals("Record{1:2:3}", c3.apply(null, 1, 2, 3).toString());
    }
    
    @Test
    public void testAnnotations() {
        IMethod c1 = ic.constructors().require(Meta.Unsafe.class).first().get();
        List<String> types = Arrays.asList(
            "@net.ranides.assira.annotations.Meta$Unsafe()", 
            "@java.lang.Deprecated()"
        ); 
        List<String> names = Arrays.asList(
            "net.ranides.assira.annotations.Meta$Unsafe", 
            "java.lang.Deprecated"
        );
        assertEquivalent(types, c1.annotations().list(a -> a.toString()));
        assertEquivalent(names, c1.annotations().names());
        
        Deprecated dc = AnnotationUtils.make(Deprecated.class);
        String exp = "[net.ranides.assira.annotations.Meta$Unsafe]";
        assertEquals(exp, c1.annotations().discard(Deprecated.class).names().toString());
        assertEquals(exp, c1.annotations().discard(a -> a.equals(dc)).names().toString());
    }
    
    
    @Test
    public void testCall() {
        IMethod c1 = ic.constructors().require(0).first().get();
        IMethod c2 = ic.constructors().require(2).first().get();
        IMethod c3 = ic.constructors().require(3).first().get();

        assertEquals("Record{0:0:0}", c1.call().toString());
        
        assertEquals("Record{5:6:11}", c2.call(5.1, 6.4).toString());
        assertEquals("Record{1:2:3}", c3.call(1, 2, 3).toString());
    }
    
    @Test
    public void testHandle() throws Throwable {
        MethodHandle c1 = ic.constructors().require(0).first().get().handle();
        MethodHandle c2 = ic.constructors().require(2).first().get().handle();
        MethodHandle c3 = ic.constructors().require(3).first().get().handle();

        assertEquals("Record{0:0:0}", c1.invoke().toString());
        
        assertEquals("Record{5:6:11}", c2.invoke(5.1, 6.4).toString());
        assertEquals("Record{1:2:3}", c3.invoke(1, 2, 3).toString());
    }
    
    @Ignore
    @Test
    public void testMatchGeneric() {
        // @todo (assira #6) reflective: generics: match
        // mamy konstruktor public <R extends Number> Record(R param1, R param2) {
        // nie wiem czy istnieje jakakolwiek szansa, żeby w oparciu o type-bounds match zaimplementować
        // chyba takie "super-elastic" metody będą po prostu bezużyteczne w przypadku reflective IClass
        // normalnie to dużo prostsze przypadki nie przechodzą, więc płaczu nie ma
        IMethods list1 = ic.constructors().require(IArguments.typeinfo(Number.class, Number.class));
        IMethods list2 = ic.constructors().matches(IClasses.typeinfo(Number.class, Number.class));
        IMethods list3 = ic.constructors().accepts(0,0);
        
        List<IMethod> exp1 = ic.constructors().require(Deprecated.class).list();

//        System.out.printf("RConstructorTest: ISSUE: %s%n", list1.list());
//        System.out.printf("RConstructorTest: ISSUE: %s%n", list2.list());
//        System.out.printf("RConstructorTest: ISSUE: %s%n", list3.list());
        
        assertEquals(exp1, list1.list());
        assertEquals(exp1, list2.list());
        assertEquals(exp1, list3.list());
    }
    
}
