package com.tinkerpop.blueprints;

import com.tinkerpop.blueprints.util.FeatureDescriptor;
import org.junit.Test;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Field;
import java.util.Arrays;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
 * All features should be prefixed with the word "supports" and return boolean.  Furthermore, all should have an
 * FeatureDescriptor annotation with a "name" that represents the suffix after "supports" in the same case as the
 * method.  A String name of the feature should be supplied as a public static variable and be equal to the value
 * of the "name" in all upper case.
 *
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public class FeaturesConventionTest {
    private static final String FEATURE_METHOD_PREFIX = "supports";
    private static final String FEATURE_FIELD_PREFIX = "FEATURE_";

    private static final String ERROR_FIELD = "Feature [%s] must have a field decleared with the name of the feature as 'public static final'";

    @Test
    public void shouldFollowConventionsForGraphFeatures() {
        final Class<Graph.Features> featuresClass = Graph.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForPropertyFeatures() {
        final Class<Property.Features> featuresClass = Property.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForEdgeFeatures() {
        final Class<Edge.Features> featuresClass = Edge.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForTransactionFeatures() {
        final Class<Transaction.Features> featuresClass = Transaction.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForVertexFeatures() {
        final Class<Vertex.Features> featuresClass = Vertex.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    private static String convertToUnderscore(final String text) {
        final String regex = "([a-z])([A-Z])";
        final String replacement = "$1_$2";
        final String underscored = text.replaceAll(regex, replacement);
        return underscored.substring(0, underscored.length());
    }

    private static boolean chooseFeatureMethod(Method m) {
        return m.getName().startsWith(FEATURE_METHOD_PREFIX) && !m.getName().equals(FEATURE_METHOD_PREFIX);
    }

    private static void assertFeatureConvention(final Method m) {
        final FeatureDescriptor annotation = m.getAnnotation(FeatureDescriptor.class);
        final Class featuresClass = m.getDeclaringClass();

        // all "features" need an annotation
        assertNotNull(annotation);

        // needs to match pattern of "support" followed by "name" value in annotation
        assertEquals(m.getName(), FEATURE_METHOD_PREFIX + annotation.name());

        try {
            final Field f = featuresClass.getField(FEATURE_FIELD_PREFIX + convertToUnderscore(annotation.name()).toUpperCase());
            assertEquals(annotation.name(), f.get(null));

            final int modifier = f.getModifiers();
            assertTrue(Modifier.isFinal(modifier));
            assertTrue(Modifier.isPublic(modifier));
            assertTrue(Modifier.isStatic(modifier));
        } catch (Exception e) {
            fail(String.format(ERROR_FIELD, annotation.name()));
        }
    }
}

@@ -4106,218 +4106,8 @@
 l));
-%0A%0A            final int modifier = f.getModifiers();%0A            assertTrue(Modifier.isFinal(modifier));%0A            assertTrue(Modifier.isPublic(modifier));%0A            assertTrue(Modifier.isStatic(modifier));
 %0A   

remove checks for modifiers.