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

import java.util.Arrays;
import java.util.List;
import net.ranides.assira.collection.maps.OrderedMap;
import net.ranides.assira.collection.maps.RandomAccessMap;
import net.ranides.assira.generic.TypeToken;
import net.ranides.test.mockup.reflection.ForIArguments;
import org.junit.Test;
import static net.ranides.assira.junit.NewAssert.*;
import static net.ranides.assira.reflection.IAttribute.*;
import static net.ranides.assira.reflection.IAttributes.*;

import net.ranides.test.mockup.reflection.ForIArguments.AboutParam;
import net.ranides.test.mockup.reflection.ForMethodUtils;

import net.ranides.assira.reflection.util.AnnotationBuilder;
import net.ranides.assira.reflection.util.AnnotationUtils;

/**
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class IArgumentsTest {
    
    IClass<?> ic = IClass.typeinfo(ForIArguments.class);
    
    @Test
    public void testMethods() {
        IArguments a1 = IArguments.typeinfo(new TypeToken<List<String>>(){});
        IArguments a2 = IArguments.typeinfo(new TypeToken<List<Integer>>(){});
        IArguments a3 = IArguments.typeinfo(
            IClass.typeinfo(int.class), 
            IClass.typeinfo(int.class)
        );
        IArguments a4 = IArguments.typeinfo(float.class, float.class);
                
        OrderedMap<String, IClass<?>> args5 = new RandomAccessMap<>();
        args5.put("p4", IClass.typeinfo(char.class));
        args5.put("p2", IClass.typeinfo(boolean.class));
        IArguments a5 = IArguments.typeinfo(args5);
        
        OrderedMap<String, IClass<?>> args6 = new RandomAccessMap<>();
        args6.put("p4", IClass.typeinfo(boolean.class));
        args6.put("p2", IClass.typeinfo(char.class));
        IArguments a6 = IArguments.typeinfo(args6);

        assertEquals(Arrays.asList("fun6"), ic.methods().require(a1).names());
        assertEquals(Arrays.asList("fun5"), ic.methods().require(a2).names());
        assertEquals(Arrays.asList("fun1"), ic.methods().require(a3).names());
        assertEquals(Arrays.asList("fun2"), ic.methods().require(a4).names());
        assertEquals(Arrays.asList("fun7"), ic.methods().require(a5).names());
        assertEquals(Arrays.asList("fun8"), ic.methods().require(a6).names());
    }
    
    @Test
    public void testNames() {
        softAssertEquals(Arrays.asList("a","b"), ic.method("fun1").get().arguments().names());
        softAssertEquals(Arrays.asList("a","b"), ic.method("fun2").get().arguments().names());
        softAssertEquals(Arrays.asList("ia","ib"), ic.method("fun3").get().arguments().names());
        softAssertEquals(Arrays.asList("map"), ic.method("fun4").get().arguments().names());

        softAssertEquals(Arrays.asList(IClass.OBJECT), IClass.OBJECT.method("equals").get().arguments().types().list());
        softAssertEquals(Arrays.asList("arg0"), IClass.OBJECT.method("equals").get().arguments().names() );
    }

    @Test
    public void testParent() {
        IMethod m1 = ic.method("fun1").get();
        IMethod m2 = IClass.typeinfo(ForMethodUtils.A.class).method("fun3").get();
        IMethod m3 = IClass.typeinfo(ForMethodUtils.B.class).method("fun3").get();
        
        assertEquals(Arrays.asList(m1,m1), m1.arguments().parent().list());
        assertEquals(Arrays.asList(m2,m2), m2.arguments().parent().list());
        assertEquals(Arrays.asList(m3,m3), m3.arguments().parent().list());
        
        assertEquals(ic, m1.arguments().parent().parent().first().get());
        assertEquals(IClass.typeinfo(ForMethodUtils.A.class), m2.arguments().parent().parent().first().get());
        assertEquals(IClass.typeinfo(ForMethodUtils.Root.class), m3.arguments().parent().parent().first().get());
        
    }
    
    @Test
    public void testMap() {
        IMethod m1 = ic.method("fun1").get();
        IMethod m2 = IClass.typeinfo(ForMethodUtils.A.class).method("fun3").get();
        IMethod m3 = IClass.typeinfo(ForMethodUtils.B.class).method("fun3").get();
        IMethod m4 = ic.method("fun4").get();

        softAssertEquals("{a=int a, b=int b}", m1.arguments().map().toString());
        softAssertEquals("{x=int x, y=char y}", m2.arguments().map().toString());
        softAssertEquals("{x=int x, y=int y}", m3.arguments().map().toString());
        softAssertEquals("{map=java.util.Map<java.lang.Integer, java.lang.String> map}", m4.arguments().map().toString());

        softAssertEquals("[int a, int b]", m1.arguments().reflective().list().toString());
        softAssertEquals("[int x, char y]", m2.arguments().reflective().list().toString());
        softAssertEquals("[int x, int y]", m3.arguments().reflective().list().toString());
        softAssertEquals("[java.util.Map<java.lang.Integer, java.lang.String> map]", m4.arguments().reflective().list().toString());
    }

    public void softAssertEquals(Object a, Object b) {
        if(!a.equals(b)) {
            System.err.println("[WARNINIG] Unstable code.");
            System.err.println("[WARNINIG]     Expected value: " + a);
            System.err.println("[WARNINIG]     Current value:  " + b);
        }
    }
    
    @Test
    public void testAttributes() {
        List<IArgument> params = ic.method("fun9").get().arguments().list();
        
        assertEquals(of(PACKAGE, DECLARED), params.get(0).attributes());
        assertEquals(of(PACKAGE, DECLARED, PARAMETERIZED), params.get(1).attributes());
    }
    
    @Test
    public void testAnnotations() {
        List<IArgument> params = ic.method("fun9").get().arguments().list();
        Deprecated a1 = AnnotationUtils.make(Deprecated.class);
        AboutParam a2 = new AnnotationBuilder<>(AboutParam.class)
                .param("name", "yparam")
                .result();
        assertEquals(Arrays.asList(a1), params.get(0).annotations().list());
        assertEquals(Arrays.asList(a2), params.get(1).annotations().list());
        
        IArguments cparams = IArguments.typeinfo(IClass.typeinfo(boolean.class), new TypeToken<List<String>>(){});
        
        assertThrows(IReflectiveException.class, ()->{
            cparams.names().toString();
        });

        assertSymEquals(cparams.list(), params);
        assertSymEquals(cparams.list().get(0), params.get(0));
        assertSymEquals(cparams.list().get(1), params.get(1));
    }
    
    @Test
    public void testCParams() {
        IArguments cparams1 = IArguments.typeinfo(
            IClass.typeinfo(boolean.class), 
            new TypeToken<List<String>>(){}
        );
        
        assertThrows(IReflectiveException.class, ()->{
            cparams1.names().toString();
        });
        
        RandomAccessMap<String, IClass<?>> iparams2 = new RandomAccessMap<>();
        iparams2.put("name", IClass.typeinfo(boolean.class));
        iparams2.put("list", new TypeToken<List<String>>(){});
        
        IArguments cparams2 = IArguments.typeinfo(iparams2);
        assertEquals(Arrays.asList("name","list"), cparams2.names());
        
        assertEquals(of(PACKAGE, DECLARED), cparams2.list().get(0).attributes());
        assertEquals(of(PACKAGE, DECLARED, PARAMETERIZED), cparams2.list().get(1).attributes());
        
        assertEquals(IAnnotations.empty(), cparams2.list().get(0).annotations());
        assertEquals(IAnnotations.empty(), cparams2.list().get(1).annotations());
        
        assertEquals(IContext.getDefault(), cparams2.list().get(0).context());
        assertEquals(IContext.getDefault(), cparams2.list().get(1).context());

        assertThrows(IReflectiveException.class, ()->{
            cparams2.parent().list().toString();
        });
        assertThrows(IReflectiveException.class, ()->{
            cparams2.reflective().list().toString();
        });
        
    }
    
    
}
