/*
 * DynamicMethodTests.java
 *
 * Copyright (c) 2012 Mike Strobel
 *
 * This source code is subject to terms and conditions of the Apache License, Version 2.0.
 * A copy of the license can be found in the License.html file at the root of this distribution.
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the
 * Apache License, Version 2.0.
 *
 * You must not remove this notice, or any other, from this software.
 */

package com.strobel.expressions;

import com.strobel.core.VerifyArgument;
import com.strobel.reflection.DynamicMethod;
import com.strobel.reflection.Type;
import com.strobel.reflection.Types;
import org.junit.Before;
import org.junit.Test;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;

import static com.strobel.expressions.Expression.*;
import static java.lang.invoke.MethodHandles.insertArguments;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.Assert.assertTrue;

/**
 * @author Mike Strobel
 */
@SuppressWarnings("ALL")
public final class DynamicMethodTests {
    private final MethodHandles.Lookup lookup = MethodHandles.lookup();
    private final HashMap<String, Object> map = new HashMap<>();

    private final MetaProperty booleanProperty = new MetaProperty("BooleanProperty", boolean.class);

    @Before
    public void setup() {
        map.put(booleanProperty.name, true);
    }

    interface BooleanAccessor {
        boolean get(final Map<String,Object> map);
    }

    @Test
    public void testHashMapAccess() throws Throwable {
        final MethodHandle lookupHandle = getLookupHandle(booleanProperty);

        final boolean test1 = (boolean)lookupHandle.invoke(map);
        final boolean test2 = (Boolean)lookupHandle.invokeWithArguments(map);

        final ParameterExpression instance = parameter(Types.Map.makeGenericType(Types.String, Types.Object));
        
        final LambdaExpression<BooleanAccessor> accessorLambda = lambda(
            Type.of(BooleanAccessor.class),
            call(
                constant(lookupHandle, Types.MethodHandle),
                DynamicMethod.invokeExact(lookupHandle),
                instance
            ),
            instance
        );

        final BooleanAccessor accessor = accessorLambda.compile();
        final boolean result = accessor.get(map);

        assertTrue(result);
    }

    private MethodHandle getLookupHandle(final MetaProperty property) throws Throwable {
        final MethodHandle get = lookup.findVirtual(
            Map.class,
            "get",
            methodType(Object.class, Object.class)
        );

        return insertArguments(
            get.asType(
                get.type()
                   .changeReturnType(boolean.class)
                   .changeParameterType(
                       1,
                       String.class
                   )
            ),
            1,
//            map,
            property.name
        );
    }

    private final static class MetaProperty {
        final String name;
        final Class<?> type;

        private MetaProperty(final String name, final Class<?> type) {
            this.name = VerifyArgument.notNull(name, "name");
            this.type = VerifyArgument.notNull(type, "type");
        }
    }
}
