package org.simpleflatmapper.reflect.meta;


import org.junit.Test;
import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition;
import org.simpleflatmapper.test.beans.DbObject;
import org.simpleflatmapper.reflect.InstantiatorDefinition;
import org.simpleflatmapper.reflect.ReflectionService;
import org.simpleflatmapper.util.TypeReference;

import java.util.AbstractMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static org.junit.Assert.*;

public class MapClassMetaTest {


    @Test
    @SuppressWarnings("unchecked")
    public void testFindPropertyStringDbObject() {
        final ClassMeta<Map<String, DbObject>> classMeta =
                ReflectionService.newInstance().getClassMeta(new TypeReference<Map<String, DbObject>>() {}.getType());
        final PropertyFinder<Map<String, DbObject>> mapPropertyFinder = classMeta.newPropertyFinder();

        final SubPropertyMeta<?, ?, ?> k_kv_k_id =
                (SubPropertyMeta<?, ?, ?>) mapPropertyFinder.findProperty(DefaultPropertyNameMatcher.of("k_kv_k_id"));
        assertNotNull(k_kv_k_id);

        PropertyMeta<Map<String, DbObject>, Object> k_kv_k_noprop = mapPropertyFinder.findProperty(DefaultPropertyNameMatcher.of("k_kv_k_noprop"));
        assertTrue(k_kv_k_noprop instanceof MapElementPropertyMeta); // self ref

        MapElementPropertyMeta<?, ?, ?> idMeta = (MapElementPropertyMeta<?, ?, ?>) k_kv_k_id.getOwnerProperty();
        assertEquals("k_kv_k", idMeta.getKey());

        final SubPropertyMeta<Map<String, DbObject>, DbObject, Object> k_kv_k_creation_time = (SubPropertyMeta<Map<String, DbObject>, DbObject, Object>) mapPropertyFinder.findProperty(DefaultPropertyNameMatcher.of("k_kv_k_creation_time"));
        assertNotNull(k_kv_k_creation_time);
        MapElementPropertyMeta<?, ?, ?> creationTimeMeta = (MapElementPropertyMeta<?, ?, ?>) k_kv_k_creation_time.getOwnerProperty();
        assertEquals("k_kv_k", creationTimeMeta.getKey());

    }

    @Test
    public void testFindPropertyStringString() {
        final ClassMeta<Map<String, String>> classMeta =
                ReflectionService.newInstance().getClassMeta(new TypeReference<Map<String, String>>() {}.getType());
        final PropertyFinder<Map<String, String>> mapPropertyFinder = classMeta.newPropertyFinder();

        final PropertyMeta<?, ?> k_kv_k_id = mapPropertyFinder.findProperty(DefaultPropertyNameMatcher.of("k_kv_k_id"));
        assertNotNull(k_kv_k_id);
        MapElementPropertyMeta<?, ?, ?> idMeta = (MapElementPropertyMeta<?, ?, ?>) k_kv_k_id;
        assertEquals("k_kv_k_id", idMeta.getKey());


    }

    @Test
    public void testFailOnAbstractMap() throws Exception {
        try {
            ReflectionService.newInstance().getClassMeta(new TypeReference<AbstractMap<String, String>>() {}.getType());
            fail();
        } catch(IllegalArgumentException e) {
            // expected
        }
    }

    @Test
    public void testFailOnAbstractOnImplWithNoEmptyConstructor() throws Exception {
        try {
            ReflectionService.newInstance().getClassMeta(NoEmptyConstructorMap.class);
            fail();
        } catch(IllegalArgumentException e) {
            // expected
        }
    }

    @Test
    public void testFailOnUnsupportedConverter() throws Exception {
        try {
            ReflectionService.newInstance().getClassMeta(new TypeReference<Map<Map<String, String>, String>>() {}.getType());
            fail();
        } catch(IllegalArgumentException e) {
            // expected
        }
    }

    @Test
    public void testUseHashMapOnMap() throws NoSuchMethodException {
        typeMetaHasTheSpecifiedClassEmptyConstructor(
                new TypeReference<Map<String, String>>() {},
                HashMap.class);

    }

    @Test
    public void testUseConcurrentHashMapOnConcurrentMap() throws NoSuchMethodException {
        typeMetaHasTheSpecifiedClassEmptyConstructor(
                new TypeReference<ConcurrentMap<String, String>>() {},
                ConcurrentHashMap.class);
    }

    @Test
    public void testUseSpecifiedImplType() throws NoSuchMethodException {
        typeMetaHasTheSpecifiedClassEmptyConstructor(
                new TypeReference<MyHashMap>() {},
                MyHashMap.class);
    }

    static class MyHashMap extends HashMap<String, String> {

    }



    private void typeMetaHasTheSpecifiedClassEmptyConstructor(TypeReference<?> typeReference, Class<?> impl) throws NoSuchMethodException {
        final ClassMeta<Map<String, String>> classMeta =
                ReflectionService.newInstance().getClassMeta(typeReference.getType());

        assertEquals(typeReference.getType(), classMeta.getType());

        hasOneInstantiatorDefinitionWithEmptyConstructorOnImpl(classMeta, impl);
    }



    private void hasOneInstantiatorDefinitionWithEmptyConstructorOnImpl(ClassMeta<?> classMeta, Class<?> impl) throws NoSuchMethodException {
        assertTrue(classMeta instanceof MapClassMeta);
        final List<InstantiatorDefinition> instantiatorDefinitions = classMeta.getInstantiatorDefinitions();

        assertEquals(1, instantiatorDefinitions.size());
        final ExecutableInstantiatorDefinition instantiatorDefinition = (ExecutableInstantiatorDefinition) instantiatorDefinitions.get(0);

        assertEquals(0, instantiatorDefinition.getParameters().length);
        assertEquals(impl.getDeclaredConstructor(), instantiatorDefinition.getExecutable());
    }


    static class NoEmptyConstructorMap extends HashMap<String, String> {
        public NoEmptyConstructorMap(int i) {
        }
    }
}

@@ -2570,16 +2570,128 @@
 _k_id);%0A
+        assertTrue(%22Expect MapElementPropertyMeta %22 + k_kv_k_id, k_kv_k_id instanceof  MapElementPropertyMeta);%0A
         

add test debug